package com.xbongbong.paas.service.stage.execute;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.xbongbong.crm.help.StageAverageStayTimeHelp;
import com.xbongbong.i18n.util.I18nMessageUtil;
import com.xbongbong.paas.business.rule.BusinessRuleConditionHelp;
import com.xbongbong.paas.config.enums.WorkflowExecutionStatusEnum;
import com.xbongbong.paas.constant.MessageConstant;
import com.xbongbong.paas.constant.PaasConstant;
import com.xbongbong.paas.constant.TimeConstant;
import com.xbongbong.paas.data.service.CabinetFileService;
import com.xbongbong.paas.data.service.RichTextService;
import com.xbongbong.paas.domain.entity.PaasFormDataEntity;
import com.xbongbong.paas.domain.entity.PaasFormEntity;
import com.xbongbong.paas.domain.entity.PaasFormExplainEntity;
import com.xbongbong.paas.domain.entity.PaasFormRichTextEntity;
import com.xbongbong.paas.domain.entity.PaasProcessDataEntity;
import com.xbongbong.paas.domain.entity.PaasStageEntity;
import com.xbongbong.paas.domain.entity.StageLogEntity;
import com.xbongbong.paas.domain.entity.StageProcessEntity;
import com.xbongbong.paas.domain.entity.StageTimeoutRemindEntity;
import com.xbongbong.paas.domain.entity.StageWorkLogEntity;
import com.xbongbong.paas.domain.entity.StageWorkflowEntity;
import com.xbongbong.paas.domain.entity.ext.PaasAttachmentEntityExt;
import com.xbongbong.paas.domain.entity.ext.PaasFormDataEntityExt;
import com.xbongbong.paas.domain.entity.ext.PaasFormEntityExt;
import com.xbongbong.paas.enums.AddressEnum;
import com.xbongbong.paas.enums.DelEnum;
import com.xbongbong.paas.enums.EnableEnum;
import com.xbongbong.paas.enums.FieldTypeEnum;
import com.xbongbong.paas.enums.FlowStatusEnum;
import com.xbongbong.paas.enums.PlatFormEnum;
import com.xbongbong.paas.enums.StageTypeEnum;
import com.xbongbong.paas.enums.StageWorkflowTriggerTypeEnum;
import com.xbongbong.paas.field.FieldAttrEntity;
import com.xbongbong.paas.field.FilterConditionsPoJo;
import com.xbongbong.paas.help.BusinessUserTeamHelp;
import com.xbongbong.paas.help.CommonHelp;
import com.xbongbong.paas.help.FormDataHelp;
import com.xbongbong.paas.help.FormDataValidateDataHelp;
import com.xbongbong.paas.help.PaasFormHelp;
import com.xbongbong.paas.help.ProFormHelp;
import com.xbongbong.paas.help.ProGetHelp;
import com.xbongbong.paas.help.ProSaveHelp;
import com.xbongbong.paas.help.ProUpdateHelp;
import com.xbongbong.paas.help.StageDetailHelp;
import com.xbongbong.paas.help.analysis.data.FormDataExecutionEventUpdateGetAnalysisDataHelp;
import com.xbongbong.paas.help.service.impl.FormDataLinkDataUpdateGetServiceImpl;
import com.xbongbong.paas.log.MongoLogHelp;
import com.xbongbong.paas.manager.feign.ScheduleTaskFeignClient;
import com.xbongbong.paas.model.PaasFormExplainModel;
import com.xbongbong.paas.model.PaasFormModel;
import com.xbongbong.paas.model.StageProcessModel;
import com.xbongbong.paas.model.StageTimeoutRemindModel;
import com.xbongbong.paas.model.StageWorkflowModel;
import com.xbongbong.paas.pojo.StageConditionPojo;
import com.xbongbong.paas.pojo.StageMessageContentPojo;
import com.xbongbong.paas.pojo.dto.FormDataUpdateDTO;
import com.xbongbong.paas.pojo.dto.FormDataUpdateGetDTO;
import com.xbongbong.paas.pojo.dto.ParseSingleRowDataDTO;
import com.xbongbong.paas.pojo.dto.UserAndDepartmentGetDTO;
import com.xbongbong.paas.pojo.dto.ValidateDataDTO;
import com.xbongbong.paas.pojo.vo.LinkResultDataVO;
import com.xbongbong.paas.pojo.vo.ParseSingleRowDataVO;
import com.xbongbong.paas.redis.PaasRedisHelper;
import com.xbongbong.paas.redis.RedisConstant;
import com.xbongbong.paas.redis.RedisPrefixConstant;
import com.xbongbong.paas.redis.RedisTimeConstant;
import com.xbongbong.paas.service.ProModelService;
import com.xbongbong.paas.service.UserService;
import com.xbongbong.paas.service.dynamic.help.DynamicHelp;
import com.xbongbong.paas.service.stage.StageHandle;
import com.xbongbong.paas.service.stage.StageHandleFactory;
import com.xbongbong.paas.service.stage.StageJumpHandle;
import com.xbongbong.paas.service.stage.StageLogHandle;
import com.xbongbong.paas.service.stage.StageLogHandleFactory;
import com.xbongbong.paas.service.stage.jump.StageJumpFactory;
import com.xbongbong.paas.toolbox.exception.XbbException;
import com.xbongbong.paas.toolbox.util.BeanUtil;
import com.xbongbong.paas.toolbox.util.CollectionsUtil;
import com.xbongbong.paas.toolbox.util.DateTimeUtil;
import com.xbongbong.paas.toolbox.util.JsonHelperUtil;
import com.xbongbong.paas.toolbox.util.StringUtil;
import com.xbongbong.paas.toolbox.wrap.UserVO;
import com.xbongbong.paas.util.ExplainUtil;
import com.xbongbong.paas.util.FormDataUtil;
import com.xbongbong.parent.factory.StageExecuteFactory;
import com.xbongbong.parent.help.CommonPermissionHelp;
import com.xbongbong.parent.interfaces.StageExecuteHandle;
import com.xbongbong.pro.businessrule.pojo.BusinessRuleInfoPojo;
import com.xbongbong.pro.businesstage.pojo.ExecutionEventFieldListPojo;
import com.xbongbong.pro.businesstage.pojo.ExecutionEventLinkDataPojo;
import com.xbongbong.pro.businesstage.pojo.ExecutionEventPojo;
import com.xbongbong.pro.businesstage.pojo.LogInfoDealPojo;
import com.xbongbong.pro.businesstage.pojo.StageGetAllListPojo;
import com.xbongbong.pro.businesstage.pojo.StageGetOneListPojo;
import com.xbongbong.pro.businesstage.pojo.StartStagePojo;
import com.xbongbong.pro.businesstage.pojo.dto.CommonJumpDTO;
import com.xbongbong.pro.businesstage.pojo.dto.StageDetailDTO;
import com.xbongbong.pro.businesstage.pojo.dto.StageExecutionEventGetDTO;
import com.xbongbong.pro.businesstage.pojo.dto.StageExecutionEventSaveDTO;
import com.xbongbong.pro.businesstage.pojo.dto.StageFinishWorkDTO;
import com.xbongbong.pro.businesstage.pojo.dto.StageGetDTO;
import com.xbongbong.pro.businesstage.pojo.dto.StageJumpDealDTO;
import com.xbongbong.pro.businesstage.pojo.dto.StageJumpSpecialDealDTO;
import com.xbongbong.pro.businesstage.pojo.dto.StartJumpDTO;
import com.xbongbong.pro.businesstage.pojo.vo.CommonJumpVO;
import com.xbongbong.pro.businesstage.pojo.vo.StageDetailVO;
import com.xbongbong.pro.businesstage.pojo.vo.StageExecutionEventGetVO;
import com.xbongbong.pro.businesstage.pojo.vo.StageExecutionEventSaveVO;
import com.xbongbong.pro.businesstage.pojo.vo.StageFinishWorkVO;
import com.xbongbong.pro.businesstage.pojo.vo.StageGetVO;
import com.xbongbong.pro.businesstage.pojo.vo.StartJumpVO;
import com.xbongbong.pro.constant.I18nStringConstant;
import com.xbongbong.pro.constant.SymbolConstant;
import com.xbongbong.pro.domain.entity.UpdateDataEntity;
import com.xbongbong.pro.enums.ErrorCodeEnum;
import com.xbongbong.pro.enums.PaasStageEnum;
import com.xbongbong.pro.enums.PackageTypeEnum;
import com.xbongbong.pro.enums.RichTextEnum;
import com.xbongbong.pro.enums.SaasMarkEnum;
import com.xbongbong.pro.enums.errorcodes.CustomerStageErrorCodeEnum;
import com.xbongbong.pro.enums.errorcodes.SystemErrorCodeEnum;
import com.xbongbong.pro.formdata.pojo.dto.FormDataDetailDTO;
import com.xbongbong.pro.formexplain.pojo.dto.HandlerExplainDTO;
import com.xbongbong.pro.log.pojo.DetailLogPojo;
import com.xbongbong.pro.permission.pojo.dto.VerifyUpdatePermissionDTO;
import com.xbongbong.pro.thread.ThreadPoolBeanConfig;
import com.xbongbong.pro.workflow.enums.WorkflowExecutionTypeEnum;
import com.xbongbong.saas.constant.BasicConstant;
import com.xbongbong.saas.constant.ParameterConstant;
import com.xbongbong.saas.constant.StringConstant;
import com.xbongbong.saas.domain.entity.StageWorkEntity;
import com.xbongbong.saas.enums.ExecutionEventEnum;
import com.xbongbong.saas.enums.OperateModuleTypeEnum;
import com.xbongbong.saas.enums.OperateTypeEnum;
import com.xbongbong.saas.enums.XbbRefTypeEnum;
import com.xbongbong.saas.enums.business.ContractEnum;
import com.xbongbong.saas.help.PublicSeniorRuleHelp;
import com.xbongbong.saas.help.SaasUpdateHelp;
import com.xbongbong.saas.help.SaveFormatHelp;
import com.xbongbong.saas.help.StageHelp;
import com.xbongbong.saas.model.PaasFormDataModel;
import com.xbongbong.saas.model.StageWorkModel;
import com.xbongbong.saas.service.toolbox.help.UserTeamHelp;
import com.xbongbong.saas.toolbox.help.FastJsonHelper;
import com.xbongbong.schedule.enums.ScheduleTaskTypeEnum;
import com.xbongbong.schedule.pojo.ScheduleTaskRelateBusinessPOJO;
import com.xbongbong.schedule.pojo.dto.ScheduleTaskAddBatchDTO;
import com.xbongbong.schedule.pojo.dto.ScheduleTaskAddDTO;
import com.xbongbong.schedule.pojo.dto.ScheduleTaskCancelDTO;
import com.xbongbong.sys.domain.entity.DepartmentEntity;
import com.xbongbong.sys.domain.entity.UserEntity;
import com.xbongbong.sys.help.PackageHelp;
import com.xbongbong.sys.help.ProPermissionHelp;
import com.xbongbong.sys.model.UserModel;
import com.xbongbong.workflow.domain.entity.WorkflowEntity;
import com.xbongbong.workflow.domain.entity.WorkflowExecutionEntity;
import com.xbongbong.workflow.domain.entity.WorkflowNodeEntity;
import com.xbongbong.workflow.enums.WorkflowNodeTypeEnum;
import com.xbongbong.workflow.model.WorkflowExecutionModel;
import com.xbongbong.workflow.model.WorkflowModel;
import com.xbongbong.workflow.model.WorkflowNodeModel;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.context.i18n.LocaleContextHolder;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
import java.util.stream.Collectors;

/**
 * @author 吴峰
 * @date 2021/11/22 19:28
 */
@Slf4j
public abstract class AbstractStageExecuteHandle implements StageExecuteHandle {

    private static final Logger LOG = LoggerFactory.getLogger(AbstractStageExecuteHandle.class);

    @Resource
    private StageWorkModel stageWorkModel;
    @Resource
    private UserModel userModel;
    @Resource
    private BusinessRuleConditionHelp businessRuleConditionHelp;
    @Resource
    private PaasFormExplainModel paasFormExplainModel;
    @Resource
    private FormDataExecutionEventUpdateGetAnalysisDataHelp formDataExecutionEventUpdateGetAnalysisDataHelp;
    @Resource
    private PaasFormHelp paasFormHelp;
    @Resource
    private PaasFormModel paasFormModel;
    @Resource
    private SaasUpdateHelp saasUpdateHelp;
    @Resource
    private PaasFormDataModel paasFormDataModel;
    @Resource
    private CommonHelp commonHelp;
    @Resource
    private ProGetHelp proGetHelp;
    @Resource
    private ProUpdateHelp proUpdateHelp;
    @Resource
    private FormDataValidateDataHelp formDataValidateDataHelp;
    @Resource
    private StageProcessModel stageProcessModel;
    @Resource
    private ProModelService proModelService;
    @Resource
    private RichTextService richTextService;
    @Resource
    private StageDetailHelp stageDetailHelp;
    @Resource
    private ThreadPoolBeanConfig threadPoolBeanConfig;
    @Resource
    private StageExecuteFactory stageExecuteFactory;
    @Resource
    private StageJumpFactory stageJumpFactory;
    @Resource
    private StageLogHandleFactory stageLogHandleFactory;
    @Resource
    private PaasRedisHelper paasRedisHelper;
    @Resource
    private StageHandleFactory stageHandleFactory;
    @Resource
    private MongoLogHelp mongoLogHelp;
    @Resource
    private PackageHelp packageHelp;
    @Resource
    private ProFormHelp proFormHelp;
    @Resource
    private UserService userService;
    @Resource
    private FormDataLinkDataUpdateGetServiceImpl formDataLinkDataUpdateGetService;
    @Resource
    private StageWorkflowModel stageWorkflowModel;
    @Resource
    private WorkflowModel workflowModel;
    @Resource
    private WorkflowNodeModel workflowNodeModel;
    @Resource
    private WorkflowExecutionModel workflowExecutionModel;
    @Resource
    private DynamicHelp dynamicHelp;
    @Resource
    private StageTimeoutRemindModel stageTimeoutRemindModel;
    @Resource
    private ScheduleTaskFeignClient scheduleTaskFeignClient;
    @Resource
    private StageHelp stageHelp;
    @Resource
    private PublicSeniorRuleHelp publicSeniorRuleHelp;
    @Resource
    private CabinetFileService cabinetFileService;
    @Resource
    private StageAverageStayTimeHelp stageAverageStayTimeHelp;
    @Resource
    private CommonPermissionHelp commonPermissionHelp;
    @Resource
    private FormDataHelp formDataHelp;

    /**
     * 阶段跳转的时候为目标阶段生成阶段超时提醒任务，取消来源阶段未生效的阶段超时提醒任务
     *
     * @param stageJumpDealDTO
     * @param paasFormDataEntityExt
     */
    protected void addStageTimeOutRemind(StageJumpDealDTO stageJumpDealDTO, PaasFormDataEntityExt paasFormDataEntityExt) {
        try {
            // 考虑旗舰版降级的情况，一开始开通了旗舰版进行了超时提醒的设置，后面版本降级以后，虽然设置依然还在表里，但是不允许创建阶段提醒任务
            if (!packageHelp.isUltimateWithFeeType(stageJumpDealDTO.getCorpid())) {
                // 非旗舰版直接返回
                LOG.info("corpid={}的公司不是旗舰版，不支持阶段超时提醒功能", stageJumpDealDTO.getCorpid());
                return;
            }
        } catch (XbbException e) {
            LOG.error("PackageHelp.isUltimateWithFeeType", e);
            return;
        }

        // 判断阶段流程是否处于启用状态，只有当前启用的流程进行阶段跳转的时候才会生成提醒任务
        StageProcessEntity stageProcess = stageJumpDealDTO.getStageProcessEntity();
        boolean stageProcessEnable = Objects.nonNull(stageProcess) && Objects.equals(stageProcess.getEnable(), EnableEnum.OPEN.getCode());
        PaasFormEntity paasForm = paasFormModel.getByKey(stageProcess.getFormId(), stageJumpDealDTO.getCorpid());
        if (Objects.isNull(paasForm)) {
            return;
        }
        Integer saasMark = paasForm.getSaasMark();
        Integer businessType = Objects.equals(saasMark, SaasMarkEnum.SAAS.getCode()) ? paasForm.getBusinessType() : XbbRefTypeEnum.PAAS.getCode();

        PaasStageEntity targetStage = stageJumpDealDTO.getToStageEntity();
        if (Objects.nonNull(targetStage) && stageProcessEnable) {
            if (Objects.equals(targetStage.getStayLimit(), 1)) {
                long stageIntoTime = DateTimeUtil.getInt();
                long stayLimitTime = targetStage.getStayLimitTime().longValue();
                Integer stayLimitTimeUnit = targetStage.getStayLimitTimeUnit();
                Map<Integer, Integer> timeOutSetMap = new HashMap<>();
                timeOutSetMap.put(1, TimeConstant.SECONDS_PER_HOUR);
                timeOutSetMap.put(2, TimeConstant.SECONDS_PER_DAY);
                timeOutSetMap.put(3, TimeConstant.SECONDS_PER_WEEK);
                timeOutSetMap.put(4, TimeConstant.SECONDS_PER_MONTH);
                long stageTimeOutTime = stageIntoTime + stayLimitTime * timeOutSetMap.get(stayLimitTimeUnit);

                // 重复进入某个阶段的时候获取之前在这个阶段累计停留的历史时长
                long historyStayTime = 0L;
                StageLogHandle stageLogHandle = stageLogHandleFactory.getStageLogHandle(saasMark, businessType);
                Map<String, Object> stageLogParam = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                stageLogParam.put("corpid", targetStage.getCorpid());
                stageLogParam.put("formId", targetStage.getFormId());
                stageLogParam.put("stageProcessId", targetStage.getStageProcessId());
                stageLogParam.put("dataId", paasFormDataEntityExt.getId());
                stageLogParam.put("stageId", targetStage.getId());
                stageLogParam.put("del", 0);
                List<StageLogEntity> stageLogList = stageLogHandle.findEntitys(stageLogParam);
                if (CollectionsUtil.isNotEmpty(stageLogList)) {
                    StageLogEntity stageLog = stageLogList.get(0);
                    historyStayTime = Objects.nonNull(stageLog.getHistoryStayTime()) ? stageLog.getHistoryStayTime() : 0L;
                }
                // 计算超时时间的时候需要把历史停留时长计算在内
                stageTimeOutTime = stageTimeOutTime - historyStayTime;

                Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                param.put("corpid", stageJumpDealDTO.getCorpid());
                param.put("formId", stageJumpDealDTO.getFormId());
                param.put("stageProcessId", targetStage.getStageProcessId());
                param.put("stageId", targetStage.getId());
                param.put("del", 0);
                List<StageTimeoutRemindEntity> stageTimeoutRemindList = stageTimeoutRemindModel.findEntitys(param);

                List<ScheduleTaskAddDTO> addDTOList = new ArrayList<>();
                for (StageTimeoutRemindEntity remind : stageTimeoutRemindList) {
                    Long remindTime = null;
                    long intervalTime = remind.getRemindTime() * TimeConstant.SECONDS_PER_HOUR;
                    switch(remind.getRemindType()){
                        case 1:
                            remindTime = stageTimeOutTime - intervalTime;
                            break;
                        case 2:
                            remindTime = stageTimeOutTime + intervalTime;
                            break;
                        case 3:
                            // 计算停留多久后提醒的时间的时候需要把历史停留时长计算在内
                            remindTime = stageIntoTime + intervalTime - historyStayTime;
                            break;
                        default:
                            break;
                    }
                    if (Objects.nonNull(remindTime)) {
                        remindTime = remindTime < stageIntoTime ? stageIntoTime : remindTime;
                        ScheduleTaskAddDTO scheduleTaskAddDTO = new ScheduleTaskAddDTO();
                        BeanUtil.copyProperties(stageJumpDealDTO, scheduleTaskAddDTO);
                        scheduleTaskAddDTO.setFormId(stageJumpDealDTO.getFormId());
                        scheduleTaskAddDTO.setTriggerTime(remindTime);
                        scheduleTaskAddDTO.setScheduleType(ScheduleTaskTypeEnum.STAGE_TIMEOUT_REMIND.getCode());
                        scheduleTaskAddDTO.setScheduleTaskRelateBusiness(new ScheduleTaskRelateBusinessPOJO(
                                paasFormDataEntityExt.getId(), targetStage.getId(), remind.getId(), businessType, saasMark));
                        addDTOList.add(scheduleTaskAddDTO);
                    }
                }
                if (CollectionsUtil.isNotEmpty(addDTOList)) {
                    ScheduleTaskAddBatchDTO scheduleTaskAddBatchDTO = new ScheduleTaskAddBatchDTO();
                    BeanUtil.copyProperties(stageJumpDealDTO, scheduleTaskAddBatchDTO);
                    scheduleTaskAddBatchDTO.setScheduleTaskList(addDTOList);
                    LOG.info("主服务调用Schedule服务的scheduleTaskAddBatch接口");
                    scheduleTaskFeignClient.scheduleTaskAddBatch(scheduleTaskAddBatchDTO, LocaleContextHolder.getLocale().toString());
                }
            }
        }

        PaasStageEntity fromStage = stageJumpDealDTO.getFromStageEntity();
        if (!stageJumpDealDTO.getIsNew() && Objects.nonNull(fromStage)) {
            ScheduleTaskCancelDTO scheduleTaskCancelDTO = new ScheduleTaskCancelDTO();
            BeanUtil.copyProperties(stageJumpDealDTO, scheduleTaskCancelDTO);
            scheduleTaskCancelDTO.setFormId(stageJumpDealDTO.getFormId());
            scheduleTaskCancelDTO.setDataId(paasFormDataEntityExt.getId());
            scheduleTaskCancelDTO.setRefId(fromStage.getId());
            scheduleTaskCancelDTO.setBusinessType(businessType);
            scheduleTaskCancelDTO.setScheduleType(ScheduleTaskTypeEnum.STAGE_TIMEOUT_REMIND.getCode());
            scheduleTaskCancelDTO.setSaasMark(saasMark);
            scheduleTaskFeignClient.scheduleTaskCancel(scheduleTaskCancelDTO, LocaleContextHolder.getLocale().toString());
        }
    }

    /**
     * description:  获取阶段条的通用方法
     * date: 2021/12/31 10:32 上午
     *
     * @Author yingjf
     */
    protected StageGetVO stageGet(Integer stageRation, PaasFormDataEntityExt paasFormDataEntityExt, StageProcessEntity stageProcessEntity, StageGetDTO stageGetDTO, List<? extends StageLogEntity> stageLogEntities, List<? extends PaasStageEntity> stageEntities,
                                    Integer approveFlowStatus, Integer workflowFlowStatus) throws XbbException {
        StageGetVO stageGetVO = new StageGetVO();
        stageGetVO.setStageShowType(2);
        StartStagePojo startStagePojo = new StartStagePojo();
        startStagePojo.setStatus(3);
        stageGetVO.setStartStage(startStagePojo);
        String corpid = stageGetDTO.getCorpid();
        Long formId = stageGetDTO.getFormId();
        Integer businessType = stageGetDTO.getBusinessType();
        Integer saasMark = stageGetDTO.getSaasMark();
        Long stageProcessId = stageProcessEntity.getId();
        Integer approveType = stageGetDTO.getApproveType();
        String platform = stageGetDTO.getPlatform();
        //是否审批阶段详情
        boolean isApprove = Objects.equals(approveType, BasicConstant.ONE) || Objects.equals(approveType, BasicConstant.TWO);
        // 阶段用时（0不显示、1显示）
        Integer showTimeUse = stageProcessEntity.getShowTimeUse();
        // 阶段停留时长（0不显示、1显示）
        Integer showTimeStay = stageProcessEntity.getShowTimeStay();
        // 任务完成情况（0不显示、1显示）
        Integer showWorkCompletion = stageProcessEntity.getShowWorkCompletion();
        // 阶段停留单位【1时、2天、3周、4月】
        Integer stageUnit = stageProcessEntity.getStageUnit();
        // 公司平均完成时长（0不显示、1显示
        Integer showAverageCompletionTime = stageProcessEntity.getShowAverageCompletionTime();
        //判断是否为白名单
        boolean isWhite = stageHelp.isSupportStageStayTime(corpid);
        //获取套餐类型
        Integer feeType = packageHelp.getFeeType(corpid);
        boolean packageSatisfy = Objects.equals(feeType, PackageTypeEnum.ULTIMATE.getType());

        Map<String, StageLogEntity> stageLogMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        if (CollectionsUtil.isNotEmpty(stageLogEntities)) {
            for (StageLogEntity stageLogEntity : stageLogEntities) {
                String stageCode = stageLogEntity.getStageCode();
                stageLogMap.put(stageCode, stageLogEntity);
            }
        }
        List<StageGetAllListPojo> stageList = new ArrayList<>();
        List<Long> stageIdIn = stageEntities.stream().map(PaasStageEntity::getId).collect(Collectors.toList());
        List<Long> stageLogIdIn = stageLogEntities.stream().map(StageLogEntity::getId).collect(Collectors.toList());

        Map<String, Object> params = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        params.put("corpid", corpid);
        params.put("formId", formId);
        params.put("stageIdIn", stageIdIn);
        params.put("stageProcessId", stageProcessId);
        params.put("del", 0);
        List<StageWorkEntity> workEntityAllList = stageWorkModel.findEntitys(params);
        Map<Long, List<StageWorkEntity>> stageWorkMap = workEntityAllList.stream().
                collect(Collectors.toMap(StageWorkEntity::getStageId,
                        stageWorkEntity -> new ArrayList<>(Arrays.asList(stageWorkEntity)),
                        (List<StageWorkEntity> oldList, List<StageWorkEntity> newList) -> {
                            oldList.addAll(newList);
                            return oldList;
                        }));
        List<Long> workIdIn = workEntityAllList.stream().map(StageWorkEntity::getId).collect(Collectors.toList());
        Map<Long, List<StageWorkLogEntity>> stageWorkLogMap = new HashMap<>();
        if (CollectionUtils.isNotEmpty(stageLogIdIn) && CollectionUtils.isNotEmpty(workIdIn)) {
            params.clear();
            params.put("corpid", corpid);
            params.put("del", 0);
            params.put("stageLogIdIn", stageLogIdIn);
            params.put("workIdIn", workIdIn);
            List<StageWorkLogEntity> stageWorkLogEntityList = stageWorkLogFindEntitys(params);
            stageWorkLogMap = stageWorkLogEntityList.stream().
                    collect(Collectors.toMap(StageWorkLogEntity::getStageLogId,
                            stageWorkLogEntity -> new ArrayList<>(Arrays.asList(stageWorkLogEntity)),
                            (List<StageWorkLogEntity> oldList, List<StageWorkLogEntity> newList) -> {
                                oldList.addAll(newList);
                                return oldList;
                            }));
        }
        Long isNowStageId = 0L;
        String isNowStageCode = "";
        String preStageCode = null;
        List<StageLogEntity> isNowStageLogList = stageLogEntities.stream().filter(stageLogEntity -> Objects.equals(stageLogEntity.getIsNow(), 1)).collect(Collectors.toList());

        PaasFormExplainEntity explain = proFormHelp.getExplainEntity(corpid, formId, businessType);
        if (Objects.isNull(explain)) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.FORM_EXPLAIN_NOT_EXIST);
        }
        FieldAttrEntity stageAttrEntity = ExplainUtil.getPassStageAttr(explain);
        if (Objects.isNull(stageAttrEntity)) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.THIS_EXPLAIN_NOT_EXIST);
        }
        if (isApprove) {
            isNowStageCode = paasFormDataEntityExt.getData().getString(stageAttrEntity.getAttr());
            preStageCode = getBeforeStageCode(approveType, isNowStageLogList, approveFlowStatus, workflowFlowStatus);
        } else if (!isNowStageLogList.isEmpty()) {
            StageLogEntity isNowStageLog = isNowStageLogList.get(0);
            isNowStageCode = isNowStageLog.getStageCode();
            preStageCode = isNowStageLog.getBeforeStageCode();
        }
        Integer isNowStageSort = null;
        Integer isNowStageType = null;
        Integer preStageSort = null;
        boolean isNowFailStage = false;
        for (PaasStageEntity stageEntity : stageEntities) {
            if (Objects.equals(stageEntity.getCode(), isNowStageCode)) {
                isNowStageId = stageEntity.getId();
                isNowStageSort = stageEntity.getSort();
                isNowStageType = stageEntity.getType();
                isNowFailStage = Objects.equals(isNowStageType, StageTypeEnum.FAIL_STAGE.getType());
            }
            if (Objects.nonNull(preStageCode) && Objects.equals(preStageCode, stageEntity.getCode())) {
                preStageSort = stageEntity.getSort();
            }
        }
        for (PaasStageEntity paasStageEntity : stageEntities) {
            //当前阶段是结束阶段时，不展示其他结束阶段
            boolean isOtherEndStage = isOtherEndStage(isNowStageId, isNowStageType, paasStageEntity.getId(), paasStageEntity.getType());
            if (isOtherEndStage) {
                continue;
            }
            //阶段推进器历史数据的阶段在阶段设置端可以被禁用
            if (Objects.equals(paasStageEntity.getEnable(), BasicConstant.ONE)) {
                StageGetAllListPojo stageListPojo = new StageGetAllListPojo();
                stageList.add(stageListPojo);
                String code = paasStageEntity.getCode();
                String name = paasStageEntity.getName();
                Long stageId = paasStageEntity.getId();
                Integer stageSort = paasStageEntity.getSort();
                Integer stageType = paasStageEntity.getType();
                StageLogEntity stageLogEntity = stageLogMap.get(code);
                Long outTime = 0L;
                Long intoTime = 0L;
                Long historyStayTime = 0L;
                Long stageLogId = null;
                if (Objects.nonNull(stageLogEntity)) {
                    outTime = stageLogEntity.getOutTime();
                    intoTime = stageLogEntity.getIntoTime();
                    historyStayTime = stageLogEntity.getHistoryStayTime();
                    stageLogId = stageLogEntity.getId();
                }
                // 悬浮展示的
                List<Map> suspension = isApprove ? null : new ArrayList<>();
                // 阶段用时
                String showTimeUseValue = "";
                // 阶段停留时长
                String showTimeStayValue = "";
                // 任务完成情况
                String showWorkCompletionValue = "";
                // 公司平均完成时长
                String showAverageCompletionTimeValue = "";
                // 当前阶段处理    阶段比例显示为上个阶段的跳出比例加当前阶段的任务完成比例
                // 获取当前阶段下的阶段任务
                List<StageWorkEntity> workEntityList = stageWorkMap.get(stageId);
                List<StageWorkLogEntity> finishedWorkLogEntityList = new ArrayList<>();
                if (Objects.nonNull(stageWorkLogMap.get(stageLogId))) {
                    finishedWorkLogEntityList = stageWorkLogMap.get(stageLogId).stream()
                            .filter(workLog -> Objects.equals(workLog.getIsFinished(), 1)).collect(Collectors.toList());
                }
                if (Objects.equals(isNowStageSort, stageSort)) {
                    stageListPojo.setStatus(2);
                    // 阶段用时
                    showTimeUseValue = stageAverageStayTimeHelp.formatTimeUse(intoTime) + " " + I18nMessageUtil.getMessage(I18nStringConstant.TO);
                    // 阶段停留时长
                    showTimeStayValue = stageAverageStayTimeHelp.secondToTime(historyStayTime + DateTimeUtil.getInt() - intoTime, stageUnit);
                    //判断白名单,是否开启预警颜色开关
                    if (isWhite && Objects.equals(paasStageEntity.getEnableStageWarningColor(), BasicConstant.ONE)) {
                        // 判断是否超时，后进行阶段停留超时提醒颜色设置
                        if (stageHelp.isTimeout(paasStageEntity.getStayLimitTime(), paasStageEntity.getStayLimitTimeUnit(), intoTime, DateTimeUtil.getInt())) {
                            stageListPojo.setStageWarningColor(paasStageEntity.getStageWarningColor());
                        }
                    }
                    // 当前所处阶段的阶段比例等于主数据上的阶段比例  线索和客户的老数据需要重新计算
                    if (stageRation != null) {
                        stageListPojo.setProportion(stageRation);
                    } else {
                        stageListPojo.setProportion(stageDetailHelp.calculateProportion(corpid, formId, stageId, stageLogId, saasMark, businessType));
                    }
                } else if (Objects.nonNull(isNowStageSort) && stageSort > isNowStageSort) {
                    // 判断下是否有结束阶段在普通阶段之前的情况，此时结束阶段是不高亮的（老数据处理）
                    if (Objects.nonNull(isNowStageType) && isNowStageSort > 0 && stageType > 0) {
                        stageListPojo.setStatus(1);
                    } else {
                        //已完成的阶段
                        stageListPojo.setStatus(3);
                    }
                    // 阶段用时
                    showTimeUseValue = stageAverageStayTimeHelp.formatTimeUse(intoTime) + " " + I18nMessageUtil.getMessage(I18nStringConstant.TO) + " " + stageAverageStayTimeHelp.formatTimeUse(outTime);
                    // 阶段停留时长
                    showTimeStayValue = stageAverageStayTimeHelp.secondToTime(historyStayTime, stageUnit);

                    //判断白名单、是否开启预警颜色开关、排除未开始阶段
                    if (isWhite && Objects.equals(paasStageEntity.getEnableStageWarningColor(), BasicConstant.ONE)
                            && !Objects.equals(stageListPojo.getStatus(), BasicConstant.ONE)) {
                        //判断是否超时，后进行阶段停留超时提醒颜色设置
                        if (stageHelp.isTimeout(paasStageEntity.getStayLimitTime(), paasStageEntity.getStayLimitTimeUnit(), intoTime, outTime)) {
                            stageListPojo.setStageWarningColor(paasStageEntity.getStageWarningColor());
                        }
                    }
                } else {
                    // 还没到达的阶段
                    stageListPojo.setStatus(1);
                    // 阶段用时
                    showTimeUseValue = "-";
                    // 阶段停留时长
                    showTimeStayValue = stageAverageStayTimeHelp.secondToTime(historyStayTime, stageUnit);
                }
                // 任务完成情况
                if (CollectionUtils.isNotEmpty(workEntityList)) {
                    showWorkCompletionValue = finishedWorkLogEntityList.size() + "/" + workEntityList.size();
                } else {
                    // 没任务时给个默认文案
                    showWorkCompletionValue = "-";
                }
                // 设置阶段悬浮显示
                if (!isApprove) {
                    stageListPojo.setSuspension(suspension);
                    if (showTimeUse == 1) {
                        Map<String, Object> showTimeUseMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                        // 阶段用时（0不显示、1显示）
                        showTimeUseMap.put("code", "timeUse");
                        showTimeUseMap.put("name", I18nMessageUtil.getMessage(I18nStringConstant.STAGE_TIME_USE));
                        showTimeUseMap.put("value", showTimeUseValue);
                        suspension.add(showTimeUseMap);
                    }
                    if (showTimeStay == 1) {
                        Map<String, Object> showTimeStayMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                        // 阶段停留时长（0不显示、1显示）
                        showTimeStayMap.put("code", "timeStay");
                        showTimeStayMap.put("name", I18nMessageUtil.getMessage(I18nStringConstant.STAGE_LENGTH_OF_STAY));
                        showTimeStayMap.put("value", showTimeStayValue);
                        suspension.add(showTimeStayMap);
                    }
                    if (showWorkCompletion == 1) {
                        Map<String, Object> showWorkCompletionMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                        // 任务完成情况（0不显示、1显示）
                        showWorkCompletionMap.put("code", "workCompletion");
                        showWorkCompletionMap.put("name", I18nMessageUtil.getMessage(I18nStringConstant.TASK_COMPLETION));
                        showWorkCompletionMap.put("value", showWorkCompletionValue);
                        suspension.add(showWorkCompletionMap);
                    }
                    if (showAverageCompletionTime == 1) {
                        Map<String, Object> showAverageCompletionTimeMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                        // 公司平均完成时长（0不显示、1显示
                        // 计算公司平均停留时长 走redis 非实时
                        try {
                            Long avgStayTime = calculateAvgStayTime(saasMark, businessType, corpid, formId, stageProcessId, stageId);
                            stageUnit =  Objects.isNull(stageUnit) ? 1 : stageUnit;
                            showAverageCompletionTimeValue = stageAverageStayTimeHelp.secondToTime(avgStayTime, stageUnit);
                        } catch (Exception e) {
                            log.error("计算公司平均停留时长showAverageCompletionTimeValue出错", e);
                            showAverageCompletionTimeValue = "";
                        }
                        showAverageCompletionTimeMap.put("code", "averageCompletionTime");
                        showAverageCompletionTimeMap.put("name", I18nMessageUtil.getMessage(I18nStringConstant.AVERGE_COMPLETION_TIME_OF_THE_COMPANY));
                        showAverageCompletionTimeMap.put("value", showAverageCompletionTimeValue);
                        suspension.add(showAverageCompletionTimeMap);
                    }
                }
                //如果为机会的输单阶段，添加是否弹出填写流失原因的提醒
                if (Objects.equals(XbbRefTypeEnum.SALES_OPPORTUNITY.getCode(), businessType)
                        && Objects.equals(StageTypeEnum.FAIL_STAGE.getType(), paasStageEntity.getType())) {
                    JSONObject extraSetting = paasStageEntity.getExtraSetting();
                    if (Objects.isNull(extraSetting) || Objects.isNull(extraSetting.getInteger("writeLossReason"))) {
                        stageListPojo.setWriteLossReason(0);
                    } else {
                        Integer writeLossReason = extraSetting.getInteger("writeLossReason");
                        stageListPojo.setWriteLossReason(writeLossReason);
                    }
                }

                //普通阶段跳转到失败阶段，中间为经过的阶段需要置灰 status 取特殊值
                if (isNowFailStage) {
                    if (Objects.isNull(preStageSort) || preStageSort > paasStageEntity.getSort()) {
                        if (isNowStageSort < paasStageEntity.getSort()) {
                            stageListPojo.setStatus(4);
                        }
                    }
                }

                //设置阶段人
                stageListPojo.setStageLogId(stageLogId);
                stageListPojo.setStageName(name);
                stageListPojo.setType(paasStageEntity.getType());
                stageListPojo.setSort(paasStageEntity.getSort());
                stageListPojo.setAllowSkipStage(paasStageEntity.getAllowSkipStage());
                stageListPojo.setAllowReactiveStage(paasStageEntity.getAllowReactiveStage());
                stageListPojo.setAllowReactive(paasStageEntity.getAllowReactive());
                stageListPojo.setStageId(stageId);

                //设置是否自动跳转
                stageListPojo.setEnableAutoJump(paasStageEntity.getEnableAutoJump());
            }
        }
        stageGetVO.setStageShowType(1);

        //非旗舰版的paas表单始终不展示阶段tab
        if (!packageSatisfy && (Objects.equals(saasMark, SaasMarkEnum.PAAS.getCode()) || (Objects.equals(saasMark, SaasMarkEnum.SAAS.getCode()) && Objects.equals(businessType, XbbRefTypeEnum.CONTRACT.getCode())))) {
            stageGetVO.setStageShowType(0);
            return stageGetVO;
        }
        // 1. 有阶段字段  没有isNow的阶段
        // 2. 没有阶段 没有isNow的阶段
        if (Objects.equals(isNowStageId, 0L)) {
            JSONArray triggerCondition = stageProcessEntity.getTriggerCondition();
            if (triggerCondition == null || isApprove) {
                startStagePojo.setStatus(2);
                startStagePojo.setProportion(0);
            } else {
                List<FilterConditionsPoJo> triggerConditionPojos = triggerCondition.toJavaList(FilterConditionsPoJo.class);
                // fix 判断触发条件
                Map<String, FieldAttrEntity> explainMap = ExplainUtil.getExplainMap(explain.getExplains(), null);
                BusinessRuleInfoPojo businessRuleInfoPojo = new BusinessRuleInfoPojo(corpid, formId, businessType, saasMark, explainMap);
                boolean validateRes = true;
                //非旗舰版的客户机会线索表单配置的触发条件失效
                if (packageSatisfy) {
                    validateRes = businessRuleConditionHelp.validatePreCondition(triggerConditionPojos, paasFormDataEntityExt, businessRuleInfoPojo);
                }
                if (!validateRes) {
                    startStagePojo.setStatus(1);
                    startStagePojo.setProportion(0);
                    stageGetVO.setStageShowType(0);
                    return stageGetVO;
                } else {
                    startStagePojo.setProportion(0);
                    startStagePojo.setStatus(2);
                }
            }
        }
        stageGetVO.setAllowBackStage(stageProcessEntity.getAllowBack());
        stageGetVO.setProcessVersionName(stageProcessEntity.getVersionName());
        stageGetVO.setStageList(stageList);
        // 移动端单独处理
        if (Objects.equals(platform, PlatFormEnum.DINGTALK.getValue())) {
            FieldAttrEntity passStageAttr = ExplainUtil.getPassStageAttr(explain);
            stageGetVO.setStageAttrName(Objects.nonNull(passStageAttr) ? passStageAttr.getAttrName() : null);
        }
        return stageGetVO;
    }

    /**
     * 当前阶段为结束阶段，其他结束阶段不展示
     * @param isNowStageId
     * @param isNowStageType
     * @param stageId
     * @param stageType
     * @return
     */
    private boolean isOtherEndStage(Long isNowStageId, Integer isNowStageType, Long stageId, Integer stageType) {
        List<Integer> endStageTypes = new ArrayList<>(BasicConstant.DEFAULT_ACCURACY);
        endStageTypes.add(StageTypeEnum.SUCCESS_STAGE.getType());
        endStageTypes.add(StageTypeEnum.FAIL_STAGE.getType());
        endStageTypes.add(StageTypeEnum.CANCEL_STAGE.getType());
        return endStageTypes.contains(isNowStageType) && endStageTypes.contains(stageType) && !Objects.equals(isNowStageId, stageId);
    }

    /**
     * 获取进入老审批/工作流审批之前的阶段code
     * @param approveType 审批来源 1是老审批 2是工作流审批
     * @param isNowStageLogList
     * @param approveFlowStatus
     * @param workflowFlowStatus
     * @return
     */
    private String getBeforeStageCode(Integer approveType, List<StageLogEntity> isNowStageLogList, Integer approveFlowStatus, Integer workflowFlowStatus) {
        //当前进入审批的数据是否新建
        if (CollectionsUtil.isNotEmpty(isNowStageLogList)) {
            StageLogEntity stageLogEntity = isNowStageLogList.get(0);
            //老审批
            if (Objects.equals(approveType, BasicConstant.ONE)) {
                //审批通过
                if (Objects.equals(FlowStatusEnum.PASS.getType(), approveFlowStatus)) {
                    return stageLogEntity.getBeforeStageCode();
                //审批未完成
                } else {
                    return stageLogEntity.getStageCode();
                }
            //工作流审批
            }else if (Objects.equals(approveType, BasicConstant.TWO)) {
                //审批通过
                if (Objects.equals(BasicConstant.TWO, workflowFlowStatus)) {
                    return stageLogEntity.getBeforeStageCode();
                //审批未完成
                } else {
                    return stageLogEntity.getStageCode();
                }
            }
        }
        return null;
    }

    @Override
    public StageDetailVO detail(StageDetailDTO stageDetailDTO) throws XbbException {
        StageDetailVO stageDetailVO = new StageDetailVO();
        List<StageGetOneListPojo> stageGetOneListPojos = new ArrayList<>();
        stageDetailVO.setWorkList(stageGetOneListPojos);
        Integer finishWorkCount = 0;
        Long stageId = stageDetailDTO.getStageId();
        Long stageLogId = stageDetailDTO.getStageLogId();
        String corpid = stageDetailDTO.getCorpid();
        Long formId = stageDetailDTO.getFormId();
        Integer saasMark = stageDetailDTO.getSaasMark();
        Integer businessType = stageDetailDTO.getBusinessType();
        Long dataId = stageDetailDTO.getDataId();
        Long stageProcessId = stageDetailDTO.getStageProcessId();
        Long menuId = stageDetailDTO.getMenuId();
        String userId = stageDetailDTO.getUserId();
        UserVO loginUser = stageDetailDTO.getLoginUser();
        if (Objects.isNull(stageId)) {
            return stageDetailVO;
        }
        StageHandle stageHandle = stageHandleFactory.getStageHandle(saasMark, businessType);
        PaasStageEntity stageEntity = stageHandle.getByKey(stageId, corpid);
        Map<String, Object> params = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        params.put("corpid", corpid);
        params.put("formId", formId);
        params.put("stageId", stageId);
        params.put("stageProcessId", stageProcessId);
        params.put("orderByStr", "sort desc");
        params.put("del", 0);
        List<StageWorkEntity> workEntityList = stageWorkModel.findEntitys(params);
        params.clear();
        Integer isNow = 0;
        Map<Long, StageWorkLogEntity> stageWorkLogMap = new HashMap<>();
        List<StageWorkLogEntity> stageWorkLogEntityList = new ArrayList<>();
        Integer approveType = stageDetailDTO.getApproveType();
        //获取套餐类型
        Integer feeType = packageHelp.getFeeType(corpid);
        boolean packageSatisfy = Objects.equals(feeType, PackageTypeEnum.ULTIMATE.getType());
        boolean isApprove = Objects.equals(approveType, BasicConstant.ONE) || Objects.equals(approveType, BasicConstant.TWO);
        if (stageLogId != null) {
            params.put("corpid", corpid);
            params.put("stageId", stageId);
            params.put("stageLogId", stageLogId);
            params.put("del", 0);
            stageWorkLogEntityList = stageWorkLogFindEntitys(params);
            stageWorkLogMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            //非当前阶段
            StageLogHandle stageLogHandle = stageLogHandleFactory.getStageLogHandle(saasMark, businessType);
            StageLogEntity stageLogEntity = stageLogHandle.get(stageLogId, corpid);
            if (Objects.nonNull(stageLogEntity)) {
                isNow = stageLogEntity.getIsNow();
            }
        }
        for (StageWorkLogEntity temp : stageWorkLogEntityList) {
            stageWorkLogMap.put(temp.getStageWorkId(), temp);
        }
        for (StageWorkEntity temp : workEntityList) {
            StageGetOneListPojo stageGetOneListPojo = new StageGetOneListPojo();
            StageWorkLogEntity stageWorkLogEntity = stageWorkLogMap.get(temp.getId());
            Integer isFinished = 0;
            Long finishedTime = null;
            if (stageWorkLogEntity != null) {
                isFinished = stageWorkLogEntity.getIsFinished();
                finishedTime = stageWorkLogEntity.getFinishedTime();
                String finishUserId = stageWorkLogEntity.getUserId();
                // 获取完成人名字
                Map<String, Object> map = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                map.put("corpid", corpid);
                map.put("userId", finishUserId);
                map.put("del", 0);
                List<UserEntity> userEntities = userModel.findEntitys(map);
                if (Objects.equals(isFinished, 1)) {
                    finishWorkCount++;
                    if (CollectionUtils.isNotEmpty(userEntities)) {
                        String userName = userEntities.get(0).getName();
                        stageGetOneListPojo.setUserName(userName);
                    }
                }
            }
            // 任务名称
            String name = temp.getName();
            // 任务描述
            String description = temp.getDescription();
            // 任务占比
            Integer proportion = temp.getProportion();
            // 是否必填
            Integer required = temp.getRequired();
            // 执行事件
            JSONObject executionEvent = temp.getExecutionEvent();
            ExecutionEventPojo executionEventPojo = JSON.toJavaObject(executionEvent, ExecutionEventPojo.class);
            Integer executionEventType = 0;
            if (executionEventPojo != null && packageSatisfy) {
                executionEventType = executionEventPojo.getType();
                String executionEventName = executionEventPojo.getName();
                if (Objects.equals(executionEventType, ExecutionEventEnum.UPLOAD_IMAGE.getType())) {
                    stageGetOneListPojo.setImages(getImagesVO(stageWorkLogEntity));
                } else if (Objects.equals(executionEventType, ExecutionEventEnum.UPLOAD_ATTACHMENT.getType())) {
                    if (stageWorkLogEntity != null) {
                        String attachment = stageWorkLogEntity.getAttachment();
                        List<PaasAttachmentEntityExt> attachments = JsonHelperUtil.parseArray(attachment, PaasAttachmentEntityExt.class);
                        stageGetOneListPojo.setAttachments(attachments);
                    } else {
                        stageGetOneListPojo.setAttachments(new ArrayList<>());

                    }
                }
                stageGetOneListPojo.setExecutionEventName(executionEventName);
            }
            JSONArray executor = temp.getExecutor();
            boolean checkUser = true;
            if (isApprove) {
                checkUser = false;
            } else {
                if (packageSatisfy && CollectionUtils.isNotEmpty(executor)) {
                    // fix:  非任务执行人不允许编辑
                    checkUser = stageDetailHelp.checkUser(corpid, saasMark, businessType, dataId, executor, userId);
                } else {
                    // 当任务执行人为空 校验表单编辑权限
                    VerifyUpdatePermissionDTO verifyUpdatePermissionDTO = new VerifyUpdatePermissionDTO();
                    BeanUtil.copyProperties(stageDetailDTO, verifyUpdatePermissionDTO);
                    try {
                        ProPermissionHelp.verifyEditPermission(verifyUpdatePermissionDTO);
                    } catch (XbbException e) {
                        checkUser = false;
                    }
                    if (checkUser) {
                        // 如果有表单编辑权限，再校验团队成员的编辑权限
                        PaasFormDataEntityExt formDataEntity = formDataHelp.getFormDataWithTeam(corpid, saasMark, businessType, dataId);
                        verifyUpdatePermissionDTO.setPaasFormDataEntityExt(formDataEntity);
                        checkUser = commonPermissionHelp.checkEditPermissionWithoutTeamEdit(verifyUpdatePermissionDTO);
                    }
                }
            }
            stageGetOneListPojo.setName(name);
            stageGetOneListPojo.setDescription(description);
            stageGetOneListPojo.setProportion(proportion);
            stageGetOneListPojo.setRequired(required);
            stageGetOneListPojo.setIsFinished(isFinished);
            stageGetOneListPojo.setExecutionEventType(executionEventType);
            // 详情页中当前阶段和有权限的人才可以完成任务
            stageGetOneListPojo.setEditable((isNow == 1 && checkUser && Objects.isNull(stageDetailDTO.getProcessTaskId())) ? 1 : 0);
            stageGetOneListPojo.setStageWorkId(temp.getId());
            if (Objects.equals(isFinished, 1)) {
                if (finishedTime != null) {
                    stageGetOneListPojo.setFinishedTime(DateTimeUtil.getStringEpochSecond(finishedTime, DateTimeUtil.SDFYMDHM));
                }
            }
            stageGetOneListPojos.add(stageGetOneListPojo);
        }
        stageDetailVO.setRangeProportion(stageEntity.getOutProportion() - stageEntity.getIntoProportion());
        stageDetailVO.setFinishWorkCount(finishWorkCount);
        return stageDetailVO;
    }

    /**
     * description:  勾选任务通用方法
     * date: 2021/12/31 10:44 上午
     *
     * @Author yingjf
     */
    protected StageFinishWorkVO stageFinishWork(StageFinishWorkDTO stageFinishWorkDTO) throws XbbException {
        StageFinishWorkVO stageFinishWorkVO = new StageFinishWorkVO();
        String corpid = stageFinishWorkDTO.getCorpid();
        Long stageLogId = stageFinishWorkDTO.getStageLogId();
        Long stageWorkId = stageFinishWorkDTO.getStageWorkId();
        Integer isFinished = stageFinishWorkDTO.getIsFinished();
        Long dataId = stageFinishWorkDTO.getDataId();
        String userId = stageFinishWorkDTO.getUserId();
        String userName = stageFinishWorkDTO.getLoginUser().getName();
        Integer saasMark = stageFinishWorkDTO.getSaasMark();
        Integer businessType = stageFinishWorkDTO.getBusinessType();
        Long formId = stageFinishWorkDTO.getFormId();
        Integer feeType = packageHelp.getFeeType(corpid);
        boolean packageSatisfy = Objects.equals(feeType, PackageTypeEnum.ULTIMATE.getType());
        StageWorkEntity stageWorkEntity = stageWorkModel.getByKey(stageWorkId, corpid);
        JSONArray executor = stageWorkEntity.getExecutor();
        Long stageId = stageWorkEntity.getStageId();
        String stageCode = stageWorkEntity.getStageCode();
        //执行人，空值代表所有人都允许执行
        if (CollectionsUtil.isNotEmpty(executor) && packageSatisfy) {
            // fix 校验任务执行人
            boolean checkUser = stageDetailHelp.checkUser(corpid, saasMark, businessType, dataId, executor, userId);
            if (!checkUser) {
                throw new XbbException(CustomerStageErrorCodeEnum.API_ERROR_238031);
            }
        } else {
            // 当任务执行人为空 校验表单编辑权限
            VerifyUpdatePermissionDTO verifyUpdatePermissionDTO = new VerifyUpdatePermissionDTO();
            BeanUtil.copyProperties(stageFinishWorkDTO, verifyUpdatePermissionDTO);
            ProPermissionHelp.verifyEditPermission(verifyUpdatePermissionDTO);
        }
        Map<String, Object> params = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        params.put("corpid", corpid);
        params.put("stageLogId", stageLogId);
        params.put("stageWorkId", stageWorkId);
        params.put("del", 0);
        List<StageWorkLogEntity> stageWorkLogEntityList = stageWorkLogFindEntitys(params);
        StageWorkLogEntity stageWorkLogEntity = new StageWorkLogEntity();
        long time = DateTimeUtil.getInt();
        if (CollectionUtils.isNotEmpty(stageWorkLogEntityList)) {
            // 更新
            stageWorkLogEntity = stageWorkLogEntityList.get(0);
            stageWorkLogEntity.setIsFinished(isFinished);
            stageWorkLogEntity.setUpdateTime(time);
            stageWorkLogEntity.setFinishedTime(time);
        } else {
            // 新增
            stageWorkLogEntity.setCorpid(corpid);
            stageWorkLogEntity.setStageLogId(stageLogId);
            stageWorkLogEntity.setStageId(stageId);
            stageWorkLogEntity.setStageCode(stageCode);
            stageWorkLogEntity.setStageWorkId(stageWorkId);
            stageWorkLogEntity.setIsFinished(isFinished);
            stageWorkLogEntity.setUserId(userId);
            stageWorkLogEntity.setFinishedTime(time);
        }
        if (isFinished == 1) {
            stageFinishWorkVO.setUserName(userName);
            stageFinishWorkVO.setFinishedTime(DateTimeUtil.getStringEpochSecond(time, DateTimeUtil.SDFYMDHM));
            JSONArray completionCondition = stageWorkEntity.getCompletionCondition();
            //校验套餐类型
            if (packageSatisfy) {
                // fix: 根据执行事件类型 校验图片 附件是否为空   字段编辑和关联字段校验 字段值是否为空
                JSONObject executionEvent = stageWorkEntity.getExecutionEvent();
                ExecutionEventPojo executionEventPojo = JSON.toJavaObject(executionEvent, ExecutionEventPojo.class);
                if (executionEventPojo != null) {
                    Integer executionEventType = executionEventPojo.getType();
                    if (Objects.equals(executionEventType, ExecutionEventEnum.UPLOAD_IMAGE.getType())) {
                        // 查下图片有没有
                        String image = stageWorkLogEntity.getImage();
                        List<String> images = JsonHelperUtil.parseArray(image, String.class);
                        if (CollectionsUtil.isEmpty(images)) {
                            throw new XbbException(CustomerStageErrorCodeEnum.API_ERROR_238033);
                        }
                    } else if (Objects.equals(executionEventType, ExecutionEventEnum.UPLOAD_ATTACHMENT.getType())) {
                        // 查下附件有没有
                        String attachment = stageWorkLogEntity.getAttachment();
                        List<PaasAttachmentEntityExt> attachments = JsonHelperUtil.parseArray(attachment, PaasAttachmentEntityExt.class);
                        if (CollectionsUtil.isEmpty(attachments)) {
                            throw new XbbException(CustomerStageErrorCodeEnum.API_ERROR_238033);
                        }
                    } else if (Objects.equals(executionEventType, ExecutionEventEnum.FIELDATTR_EDIT.getType())) {
                        // 查下必填字段
                        List<ExecutionEventFieldListPojo> fieldList = executionEventPojo.getFieldList();
                        PaasFormDataEntity formDataEntity = proModelService.getFormDataEntity(corpid, saasMark, businessType, dataId);
                        JSONObject data = formDataEntity.getData();
                        PaasFormExplainEntity explain = paasFormExplainModel.getByFormId(formId, corpid);
                        Map<String, FieldAttrEntity> explainMap = ExplainUtil.getExplainMap(explain.getExplains(), null);
                        ValidateDataDTO validateDataDTO  = new ValidateDataDTO();
                        validateDataDTO.setCorpid(corpid);
                        validateDataDTO.setSaasMark(saasMark);
                        validateDataDTO.setFormId(formId);
                        validateDataDTO.setBusinessType(businessType);
                        Map<String, Boolean> validateFieldMap = formDataValidateDataHelp.getValidateFieldMap(validateDataDTO, data, explainMap);

                        for (ExecutionEventFieldListPojo temp : fieldList) {
                            String attr = temp.getAttr();
                            Integer required = temp.getRequired();
                            Boolean isShow = validateFieldMap.get(attr);
                            if(isShow!=null && !isShow){
                                continue;
                            }
                            if (Objects.equals(required, 1) && Objects.equals(attr, RichTextEnum.RICH_TEXT.getAttr())){
                                PaasFormRichTextEntity richTextEntity = richTextService.getByDataId(dataId, corpid, formId, businessType, saasMark, DelEnum.NORMAL.getDel());
                                if (StringUtil.isEmpty(richTextEntity.getData())){
                                    throw new XbbException(CustomerStageErrorCodeEnum.API_ERROR_238034);
                                }
                                continue;
                            }
                            Object value = data.get(attr);
                            if (Objects.equals(required, 1) && Objects.equals(temp.getFieldType(), FieldTypeEnum.ADDRESS.getType()) && Objects.nonNull(value) && value instanceof Map) {
                                JSONObject addressObj = (JSONObject) value;
                                String province = addressObj.getString(AddressEnum.PROVINCE.alias);
                                String city = addressObj.getString(AddressEnum.CITY.getAlias());
                                String district = addressObj.getString(AddressEnum.DISTRICT.alias);
                                String address = addressObj.getString(AddressEnum.ADDRESS.alias);
                                if (StringUtil.isEmpty(province) && StringUtil.isEmpty(city) && StringUtil.isEmpty(district) && StringUtil.isEmpty(address)) {
                                    throw new XbbException(CustomerStageErrorCodeEnum.API_ERROR_238034);
                                }
                            }
                            if (Objects.equals(required, 1) &&
                                    (Objects.isNull(value) || StringUtil.isEmpty(Objects.toString(value, "")) ||
                                            (value instanceof List && data.getJSONArray(attr).isEmpty()))){
                                throw new XbbException(CustomerStageErrorCodeEnum.API_ERROR_238034);
                            }
                        }
                    } else if (Objects.equals(executionEventType, ExecutionEventEnum.LINK_DATA_EDIT.getType())) {
                        // 查下关联数据的必填字段
                        PaasFormDataEntity formDataEntity = proModelService.getFormDataEntity(corpid, saasMark, businessType, dataId);
                        ExecutionEventLinkDataPojo linkData = executionEventPojo.getLinkData();
                        List<ExecutionEventFieldListPojo> fieldList = linkData.getFieldList();
                        Integer linkDataBusinessType = linkData.getBusinessType();
                        Integer linkDataSaasMark = linkData.getSaasMark();
                        String linkAttr = linkData.getAttr();
                        Long linkDataId = formDataEntity.getData().getLong(linkAttr);

                        if (Objects.nonNull(linkDataId) && linkDataId > 0) {
                            PaasFormDataEntity linkFormDataEntity = proModelService.getFormDataEntity(corpid, linkDataSaasMark, linkDataBusinessType, linkDataId);
                            JSONObject data = linkFormDataEntity.getData();
                            Long linkFormId = linkFormDataEntity.getFormId();

                            PaasFormExplainEntity explain = paasFormExplainModel.getByFormId(linkFormId, corpid);
                            Map<String, FieldAttrEntity> explainMap = ExplainUtil.getExplainMap(explain.getExplains(), null);
                            ValidateDataDTO validateDataDTO  = new ValidateDataDTO();
                            validateDataDTO.setCorpid(corpid);
                            validateDataDTO.setSaasMark(linkDataSaasMark);
                            validateDataDTO.setFormId(linkFormId);
                            validateDataDTO.setBusinessType(linkDataBusinessType);
                            Map<String, Boolean> validateFieldMap = formDataValidateDataHelp.getValidateFieldMap(validateDataDTO, data, explainMap);


                            for (ExecutionEventFieldListPojo temp : fieldList) {
                                String attr = temp.getAttr();
                                Integer required = temp.getRequired();
                                Boolean isShow = validateFieldMap.get(attr);
                                if(isShow!=null && !isShow){
                                    continue;
                                }
                                if (Objects.equals(required, 1) && Objects.isNull(data.get(attr))) {
                                    throw new XbbException(CustomerStageErrorCodeEnum.API_ERROR_238034);
                                }
                            }
                        }
                    }
                }
            }
            // fix 校验任务完成条件  应该是和触发条件校验类似  只是多加了 提示
            if (CollectionUtils.isNotEmpty(completionCondition)) {
                List<StageConditionPojo> stageConditionPojoList = completionCondition.toJavaList(StageConditionPojo.class);
                PaasFormExplainEntity explain = paasFormExplainModel.getByFormId(formId, corpid);
                Map<String, FieldAttrEntity> explainMap = ExplainUtil.getExplainMap(explain.getExplains(), null);
                PaasFormDataEntityExt paasFormDataEntityExt = (PaasFormDataEntityExt) proModelService.getFormDataEntity(corpid, saasMark, businessType, dataId);
                for (StageConditionPojo stageConditionPojo : stageConditionPojoList) {
                    List<FilterConditionsPoJo> filter = stageConditionPojo.getFilter();
                    List<StageMessageContentPojo> messageContent = stageConditionPojo.getMessageContent();
                    BusinessRuleInfoPojo businessRuleInfoPojo = new BusinessRuleInfoPojo(corpid, formId, businessType, saasMark, explainMap);
                    boolean validateRes = businessRuleConditionHelp.validatePreCondition(filter, paasFormDataEntityExt, businessRuleInfoPojo);
                    if (!validateRes) {
                        Map<Long, Map<String, FieldAttrEntity>> formExplainMap = new HashMap<>();
                        formExplainMap.put(formId, explainMap);
                        Map<Long, PaasFormDataEntityExt> paasFormDataEntityExtMap = new HashMap<>();
                        paasFormDataEntityExtMap.put(formId, paasFormDataEntityExt);
                        String contentValue = stageDetailHelp.getContentValue(messageContent, formExplainMap, paasFormDataEntityExtMap);
                        if(StringUtil.isNotEmpty(contentValue)){
                            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, contentValue);
                        }else{
                            throw new XbbException(CustomerStageErrorCodeEnum.API_ERROR_238067);
                        }

                    }
                }
            }
        }
        stageWorkLogSave(stageWorkLogEntity);
        finishWorkLog(stageFinishWorkDTO, stageWorkEntity);
        // 更新数据的比率字段
        Integer stageRation = calculateAndUpdateStageRatioValue(corpid, saasMark, formId, businessType, dataId, stageId, stageLogId);
        stageFinishWorkVO.setProportion(stageRation);
        return stageFinishWorkVO;
    }

    /**
     * description:  完成任务获取取消任务的时候重新计算和更新阶段比例
     * date: 2021/12/3 9:50 上午
     *
     * @Author yingjf
     */
    private Integer calculateAndUpdateStageRatioValue(String corpid, Integer saasMark, Long formId, Integer businessType, Long dataId, Long stageId, Long stageLogId) throws XbbException {
        Integer stageRatioValue = stageDetailHelp.calculateProportion(corpid, formId, stageId, stageLogId, saasMark, businessType);
        JSONObject jsonObject = new JSONObject();
        jsonObject.put(PaasStageEnum.STAGE_RATIO.getAttr(), stageRatioValue);
        UpdateDataEntity updateData = ExplainUtil.getUpdateData(dataId, jsonObject, corpid);
        proUpdateHelp.updateBatch(saasMark, businessType, corpid, Collections.singletonList(updateData));
        return stageRatioValue;
    }

    @Override
    public StageExecutionEventSaveVO executionEventSave(StageExecutionEventSaveDTO stageExecutionEventSaveDTO) throws XbbException {
        Integer saasMark = stageExecutionEventSaveDTO.getSaasMark();
        Integer businessType = stageExecutionEventSaveDTO.getBusinessType();
        Integer executionEventType = stageExecutionEventSaveDTO.getExecutionEventType();
        String corpid = stageExecutionEventSaveDTO.getCorpid();
        Long stageLogId = stageExecutionEventSaveDTO.getStageLogId();
        Long stageWorkId = stageExecutionEventSaveDTO.getStageWorkId();
        String userId = stageExecutionEventSaveDTO.getUserId();
        Long dataId = stageExecutionEventSaveDTO.getDataId();
        Long formId = stageExecutionEventSaveDTO.getFormId();
        List<PaasAttachmentEntityExt> attachments = stageExecutionEventSaveDTO.getAttachments();
        Map<String, Object> params = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        StageWorkEntity stageWorkEntity = stageWorkModel.getByKey(stageWorkId, corpid);
        JSONArray executor = stageWorkEntity.getExecutor();
        Long stageId = stageWorkEntity.getStageId();
        String stageCode = stageWorkEntity.getStageCode();
        //执行人，空值代表所有人都允许执行
        if (CollectionsUtil.isNotEmpty(executor)) {
            // fix 校验任务执行人
            boolean checkUser = stageDetailHelp.checkUser(corpid, saasMark, businessType, dataId, executor, userId);
            if (!checkUser) {
                throw new XbbException(CustomerStageErrorCodeEnum.API_ERROR_238031);
            }
        }
        params.put("corpid", corpid);
        params.put("stageLogId", stageLogId);
        params.put("stageWorkId", stageWorkId);
        params.put("del", 0);
        List<StageWorkLogEntity> stageWorkLogEntityList = stageWorkLogFindEntitys(params);
        StageWorkLogEntity stageWorkLogEntity = new StageWorkLogEntity();
        StageWorkLogEntity source = new StageWorkLogEntity();
        if (CollectionUtils.isNotEmpty(stageWorkLogEntityList)) {
            // 更新
            stageWorkLogEntity = stageWorkLogEntityList.get(0);
        } else {
            // 新增
            stageWorkLogEntity.setCorpid(corpid);
            stageWorkLogEntity.setStageLogId(stageLogId);
            stageWorkLogEntity.setStageId(stageId);
            stageWorkLogEntity.setStageCode(stageCode);
            stageWorkLogEntity.setStageWorkId(stageWorkId);
            stageWorkLogEntity.setIsFinished(0);
            stageWorkLogEntity.setUserId(userId);
        }
        BeanUtil.copyProperties(stageWorkLogEntity, source);
        List<PaasAttachmentEntityExt> images = getAttachmentImages(stageExecutionEventSaveDTO.getImages(), source);
        // 空的话需要将任务取消勾选  取消勾选需要 更新比例
        Integer stageRation = null;
        if ((CollectionUtils.isEmpty(images) && Objects.equals(executionEventType, ExecutionEventEnum.UPLOAD_IMAGE.getType())) ||
                (CollectionUtils.isEmpty(attachments) && Objects.equals(executionEventType, ExecutionEventEnum.UPLOAD_ATTACHMENT.getType()))) {
            stageWorkLogEntity.setIsFinished(0);
            stageWorkLogSave(stageWorkLogEntity);
            stageRation = calculateAndUpdateStageRatioValue(corpid, saasMark, formId, businessType, dataId, stageId, stageLogId);
        }
        JSONObject executionEvent = stageWorkEntity.getExecutionEvent();
        ExecutionEventPojo executionEventPojo = JSON.toJavaObject(executionEvent, ExecutionEventPojo.class);
        ExecutionEventEnum executionEventEnum = ExecutionEventEnum.getByCode(executionEventType);
        List<DetailLogPojo> detailArr = new ArrayList<>();
        PaasFormDataEntityExt formDataEntity = null;
        switch (executionEventEnum) {
            case UPLOAD_IMAGE:
                // 更新（新增、删除） 文件柜中阶段推进器的附件
                cabinetFileService.saveDataCabinetFile4StageThruster(images, stageExecutionEventSaveDTO.getCorpid(), stageExecutionEventSaveDTO.getDataId(), stageExecutionEventSaveDTO.getBusinessType(), stageExecutionEventSaveDTO.getUserId(), stageExecutionEventSaveDTO.getStageWorkId());
                stageWorkLogEntity.setImage(JSON.toJSONString(images));
                stageWorkLogSave(stageWorkLogEntity);
                break;
            case UPLOAD_ATTACHMENT:
                attachments = stageExecutionEventSaveDTO.getAttachments();
                // 更新（新增、删除） 文件柜中阶段推进器的附件
                cabinetFileService.saveDataCabinetFile4StageThruster(attachments, stageExecutionEventSaveDTO.getCorpid(), stageExecutionEventSaveDTO.getDataId(), stageExecutionEventSaveDTO.getBusinessType(), stageExecutionEventSaveDTO.getUserId(), stageExecutionEventSaveDTO.getStageWorkId());
                stageWorkLogEntity.setAttachment(JSON.toJSONString(attachments));
                stageWorkLogSave(stageWorkLogEntity);
                break;
            case FIELDATTR_EDIT:
                formDataEntity = (PaasFormDataEntityExt) proModelService.getFormDataEntity(corpid, saasMark, businessType, dataId);
                formDataEntity.setSaasMark(saasMark);
                formDataEntity.setBusinessType(businessType);
                JSONObject oldData = formDataEntity.getData();
                ValidateDataDTO validateDataDTO = new ValidateDataDTO();
                BeanUtils.copyProperties(stageExecutionEventSaveDTO, validateDataDTO);
                List<ExecutionEventFieldListPojo> fieldList = getFieldList(executionEvent, executionEventEnum);
                formId = stageExecutionEventSaveDTO.getFormId();
                BeanUtil.copyProperties(stageExecutionEventSaveDTO, validateDataDTO);
                PaasFormExplainEntity explain = paasFormExplainModel.getByFormId(formId, corpid);
                Map<String, FieldAttrEntity> explainMap = ExplainUtil.getExplainMap(explain.getExplains(), null);
                Map<String, FieldAttrEntity> showExplainMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                JSONObject data = stageExecutionEventSaveDTO.getDataList();
                List<UpdateDataEntity> updateDataEntityList = new ArrayList<>();
                JSONObject temp = new JSONObject();
                for (ExecutionEventFieldListPojo fieldListPojo : fieldList) {
                    String attr = fieldListPojo.getAttr();
                    FieldAttrEntity fieldAttrEntity = explainMap.get(attr);
                    if (Objects.isNull(fieldAttrEntity)) {
                        continue;
                    }
                    showExplainMap.put(attr, fieldAttrEntity);
                }
                validateDataDTO.setData(data);
                validateDataDTO.setExplainMap(showExplainMap);
                // 通用数据格式转换
                ProSaveHelp.analyticalData(validateDataDTO);
                // 解析标签的格式
                SaveFormatHelp.dealLabelData(validateDataDTO.getData(), explainMap);
                // 通用数据校验
                formDataValidateDataHelp.validateData(validateDataDTO);
                oldData.putAll(data);
                Map<String, Boolean> validateFieldMap = formDataValidateDataHelp.getValidateFieldMap(validateDataDTO, oldData, explainMap);
                data = validateDataDTO.getData();
                FormDataUtil.removeSystemData(data);
                for (ExecutionEventFieldListPojo fieldListPojo : fieldList) {
                    String attr = fieldListPojo.getAttr();
                    Integer required = fieldListPojo.getRequired();
                    FieldAttrEntity fieldAttrEntity = explainMap.get(attr);
                    if (Objects.isNull(fieldAttrEntity)) {
                        continue;
                    }
                    Object value = data.get(attr);
                    Object oldValue = oldData.get(attr);
                    // 选项关联是否展示
                    Boolean isShow = validateFieldMap.get(attr);
                    if (isShow!=null && !isShow){
                        continue;
                    }
                    if (Objects.equals(required, 1) && Objects.isNull(value)) {
                        //必填字段未填，不允许保存
                        throw new XbbException(CustomerStageErrorCodeEnum.API_ERROR_238049);
                    }
                    //富文本单独保存
                    if(!Objects.equals(fieldAttrEntity.getFieldType(), FieldTypeEnum.RICH_TEXT.getType())){
                        temp.put(attr, value);
                        detailArr.add(new DetailLogPojo(fieldAttrEntity.getAttr(), fieldAttrEntity.getAttrName(),
                                Objects.isNull(oldValue) ? "" : oldValue.toString(), Objects.isNull(value) ? "" : value.toString()));
                    }else{
                        richTextService.updateFormRichText(new FormDataUpdateDTO(dataId, corpid, formId, businessType, saasMark), value);
                        detailArr.add(new DetailLogPojo(fieldAttrEntity.getAttr(), fieldAttrEntity.getAttrName(),
                                 "", "" ));
                    }
                }
                UpdateDataEntity updateData = ExplainUtil.getUpdateData(dataId, temp, corpid);
                updateDataEntityList.add(updateData);
                proUpdateHelp.updateBatch(saasMark, businessType, corpid, updateDataEntityList);
                cabinetFileService.saveDataCabinetFile(showExplainMap, data, null, corpid, dataId, businessType, userId);
                break;
            case LINK_DATA_EDIT:
                PaasFormDataEntityExt thisFormDataEntity = (PaasFormDataEntityExt) proModelService.getFormDataEntity(corpid, saasMark, businessType, dataId);
                JSONObject thisData = thisFormDataEntity.getData();
                validateDataDTO = new ValidateDataDTO();
                BeanUtils.copyProperties(stageExecutionEventSaveDTO, validateDataDTO);
                fieldList = getFieldList(executionEvent, executionEventEnum);
                Long linkFormId = executionEventPojo.getLinkData().getFormId();
                Integer linkSaasMark = executionEventPojo.getLinkData().getSaasMark();
                Integer linkBusinessType = executionEventPojo.getLinkData().getBusinessType();
                String linkAttr = executionEventPojo.getLinkData().getAttr();
                Long linkDataId = thisData.getLong(linkAttr);

                PaasFormDataEntityExt linkFormDataEntity = (PaasFormDataEntityExt) proModelService.getFormDataEntity(corpid, linkSaasMark, linkBusinessType, linkDataId);
                oldData = linkFormDataEntity.getData();

                BeanUtil.copyProperties(stageExecutionEventSaveDTO, validateDataDTO);
                explain = proFormHelp.getExplainEntity(corpid, linkFormId, linkBusinessType);
                explainMap = ExplainUtil.getExplainMap(explain.getExplains(), null);
                showExplainMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                data = stageExecutionEventSaveDTO.getDataList();


                updateDataEntityList = new ArrayList<>();
                temp = new JSONObject();
                for (ExecutionEventFieldListPojo fieldListPojo : fieldList) {
                    String attr = fieldListPojo.getAttr();
                    FieldAttrEntity fieldAttrEntity = explainMap.get(attr);
                    if (Objects.isNull(fieldAttrEntity)) {
                        continue;
                    }
                    showExplainMap.put(attr, fieldAttrEntity);
                }
                validateDataDTO.setData(data);
                validateDataDTO.setExplainMap(showExplainMap);
                // 通用数据格式转换
                ProSaveHelp.analyticalData(validateDataDTO);
                // 解析标签的格式
                SaveFormatHelp.dealLabelData(validateDataDTO.getData(), explainMap);
                data = validateDataDTO.getData();
                FormDataUtil.removeSystemData(data);
                oldData.putAll(data);
                validateFieldMap = formDataValidateDataHelp.getValidateFieldMap(validateDataDTO, oldData, explainMap);
                boolean isEditConTractSignTime = false;
                for (ExecutionEventFieldListPojo fieldListPojo : fieldList) {
                    String attr = fieldListPojo.getAttr();
                    Integer required = fieldListPojo.getRequired();
                    Boolean isShow = validateFieldMap.get(attr);
                    if(isShow!=null && !isShow){
                        continue;
                    }
                    FieldAttrEntity fieldAttrEntity = explainMap.get(attr);
                    if (Objects.isNull(fieldAttrEntity)) {
                        continue;
                    }
                    if (Objects.equals(linkBusinessType,XbbRefTypeEnum.CONTRACT.getCode()) && Objects.equals(attr, ContractEnum.SIGN_TIME.getAttr())) {
                        isEditConTractSignTime = true;
                    }
                    Object value = data.get(attr);
                    Object oldValue = oldData.get(attr);
                    if (Objects.equals(required, 1) && Objects.isNull(value)) {
                        //必填字段未填，不允许保存
                        throw new XbbException(CustomerStageErrorCodeEnum.API_ERROR_238049);
                    }
                    //富文本单独保存
                    if(!Objects.equals(fieldAttrEntity.getFieldType(), FieldTypeEnum.RICH_TEXT.getType())){
                        temp.put(attr, value);
                        detailArr.add(new DetailLogPojo(fieldAttrEntity.getAttr(), fieldAttrEntity.getAttrName(),
                                Objects.isNull(oldValue) ? "" : oldValue.toString(), Objects.isNull(value) ? "" : value.toString()));
                    }else{
                        richTextService.updateFormRichText(new FormDataUpdateDTO(dataId, corpid, formId, businessType, saasMark), value);
                        detailArr.add(new DetailLogPojo(fieldAttrEntity.getAttr(), fieldAttrEntity.getAttrName(),
                                "", "" ));
                    }
                }
                updateData = ExplainUtil.getUpdateData(linkDataId, temp, corpid);
                updateDataEntityList.add(updateData);
                proUpdateHelp.updateBatch(linkSaasMark, linkBusinessType, corpid, updateDataEntityList);
                //处理签订时间的特殊逻辑
                if (isEditConTractSignTime) {
                    updateCustomerContractSignTime(linkDataId,corpid,oldData,data);
                }
                cabinetFileService.saveDataCabinetFile(showExplainMap, data, null, corpid, dataId, linkBusinessType, userId);
                // 下面日志用
                thisFormDataEntity.setBusinessType(linkBusinessType);
                thisFormDataEntity.setSaasMark(linkSaasMark);
                formDataEntity = thisFormDataEntity;
                break;
            case NULL:
                break;
            default:
                throw new XbbException(CustomerStageErrorCodeEnum.API_ERROR_238035);
        }
        updateImagesOrAttachmentsLog(stageExecutionEventSaveDTO, source, executionEventPojo);
        fieldAttrOrLinkDataEditLog(stageExecutionEventSaveDTO, detailArr, formDataEntity);
        StageExecutionEventSaveVO stageExecutionEventSaveVO = new StageExecutionEventSaveVO();
        stageExecutionEventSaveVO.setProportion(stageRation);
        return stageExecutionEventSaveVO;
    }

    @Override
    public StageExecutionEventGetVO executionEventGet(StageExecutionEventGetDTO stageExecutionEventGetDTO) throws XbbException {
        StageExecutionEventGetVO stageExecutionEventGetVO = new StageExecutionEventGetVO();
        String corpid = stageExecutionEventGetDTO.getCorpid();
        Long formId = stageExecutionEventGetDTO.getFormId();
        Long stageWorkId = stageExecutionEventGetDTO.getStageWorkId();
        String userId = stageExecutionEventGetDTO.getUserId();
        Long dataId = stageExecutionEventGetDTO.getDataId();
        Integer saasMark = stageExecutionEventGetDTO.getSaasMark();
        Integer businessType = stageExecutionEventGetDTO.getBusinessType();
        Integer subBusinessType = stageExecutionEventGetDTO.getSubBusinessType();
        StageWorkEntity stageWorkEntity = stageWorkModel.getByKey(stageWorkId, corpid);
        JSONArray executor = stageWorkEntity.getExecutor();
        JSONObject executionEvent = stageWorkEntity.getExecutionEvent();
        //执行人，空值代表所有人都允许执行
        if (CollectionsUtil.isNotEmpty(executor)) {
            // fix 校验任务执行人
            boolean checkUser = stageDetailHelp.checkUser(corpid, saasMark, businessType, dataId, executor, userId);
            if (!checkUser) {
                throw new XbbException(CustomerStageErrorCodeEnum.API_ERROR_238031);
            }
        }
        ExecutionEventPojo executionEventPojo = JSON.toJavaObject(executionEvent, ExecutionEventPojo.class);
        Integer executionEventType = executionEventPojo.getType();
        ExecutionEventEnum executionEventEnum = ExecutionEventEnum.getByCode(executionEventType);
        PaasFormDataEntityExt paasFormDataEntityExt = (PaasFormDataEntityExt) proModelService.getFormDataEntity(corpid, saasMark, businessType, dataId);
        JSONObject data = paasFormDataEntityExt.getData();
        //拼装富文本数据
        richTextService.addRichTextByDataId(dataId, corpid, formId, businessType, saasMark, DelEnum.NORMAL.getDel(), data);
        switch (executionEventEnum) {
            case FIELDATTR_EDIT:
                PaasFormExplainEntity explain = paasFormExplainModel.getByFormId(formId, corpid);
                PaasFormEntityExt paasFormEntity = paasFormModel.getByKey(formId, corpid);
                Map<String, FieldAttrEntity> explainMap = ExplainUtil.getExplainMap(explain.getExplains(), null);
                List<FieldAttrEntity> explainList = JSONArray.parseArray(explain.getExplains(), FieldAttrEntity.class);
                List<ExecutionEventFieldListPojo> fieldList = executionEventPojo.getFieldList();
                Map<String, FieldAttrEntity> showExplainMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                for (ExecutionEventFieldListPojo temp : fieldList) {
                    String attr = temp.getAttr();
                    FieldAttrEntity fieldAttrEntity = explainMap.get(attr);
                    if(Objects.isNull(fieldAttrEntity)){
                        continue;
                    }
                    fieldAttrEntity.setRequired(Integer.sum(temp.getRequired(), fieldAttrEntity.getRequired()) == 0 ? 0 : 1);
                    showExplainMap.put(attr, fieldAttrEntity);
                }
                Map<String, UserEntity> userMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                UserAndDepartmentGetDTO userAndDepartmentGetDTO = new UserAndDepartmentGetDTO();
                BeanUtil.copyProperties(stageExecutionEventGetDTO, userAndDepartmentGetDTO);
                userAndDepartmentGetDTO.setExplainList(explainList);
                Map<String, DepartmentEntity> departmentMap = commonHelp.getDepMap(userAndDepartmentGetDTO);
                HandlerExplainDTO handlerExplainDTO = new HandlerExplainDTO();
                BeanUtil.copyProperties(stageExecutionEventGetDTO, handlerExplainDTO, false);
                String creatorId = paasFormDataEntityExt.getCreatorId();
                handlerExplainDTO.setCreatorId(creatorId);
                handlerExplainDTO.setExplainList(explainList);
                // 获取协同人和负责人
                JSONObject ownerAndCoUser = paasFormHelp.getOwnerIdAndCoUserId(handlerExplainDTO, saasMark, businessType, subBusinessType, paasFormDataEntityExt, userMap);

                ParseSingleRowDataDTO parseSingleRowDataDTO = new ParseSingleRowDataDTO();
                BeanUtil.copyProperties(stageExecutionEventGetDTO, parseSingleRowDataDTO);
                parseSingleRowDataDTO.setDataJsonObject(data);
                parseSingleRowDataDTO.setExplainMap(explainMap);
                parseSingleRowDataDTO.setUserMap(userMap);
                parseSingleRowDataDTO.setDepartmentMap(departmentMap);
                ParseSingleRowDataVO parseSingleRowDataVO = formDataExecutionEventUpdateGetAnalysisDataHelp.parseSingleRowData(parseSingleRowDataDTO);
                if (parseSingleRowDataVO == null) {
                    throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.PARSE_SINGLE_ROW_DATA_FAILED);
                }
                // 没设置的字段不可见
                for (FieldAttrEntity temp : explainList) {
                    String attr = temp.getAttr();
                    FieldAttrEntity fieldAttrEntity = showExplainMap.get(attr);
                    if (Objects.isNull(fieldAttrEntity)) {
                        temp.setVisible(0);
                    } else {
                        temp.setSort(fieldAttrEntity.getSort());
                        temp.setRequired(fieldAttrEntity.getRequired());
                    }
                }
                //先获取data数据
                PaasFormDataEntityExt formData = new PaasFormDataEntityExt();
                if (Objects.equals(saasMark, SaasMarkEnum.SAAS.getCode())) {
                    formData = saasUpdateHelp.getFormData(dataId, corpid, businessType);
                } else {
                    // 没有saasMark标志的，现在全按paas应用来处理
                    formData = paasFormDataModel.getByKey(dataId, corpid);
                }
                LinkResultDataVO linkResultDataVO = parseSingleRowDataVO.getLinkResultDataVO();
                JSONObject result = parseSingleRowDataVO.getResult();
                if (linkResultDataVO != null && !linkResultDataVO.getLinkData().isEmpty() && !linkResultDataVO.getLinkDataSet().isEmpty()) {
                    FormDataUpdateGetDTO formDataGetDTO = new FormDataUpdateGetDTO();
                    BeanUtil.copyProperties(stageExecutionEventGetDTO, formDataGetDTO);
                    linkResultDataVO.setData(result);
                    linkResultDataVO.setExplainMap(explainMap);
                    linkResultDataVO.setUserMap(userMap);
                    linkResultDataVO.setDepartmentMap(departmentMap);
                    linkResultDataVO.setExplainList(explainList);
                    result = formDataLinkDataUpdateGetService.formDataLinkDataGet(formDataGetDTO, linkResultDataVO);
                }
                formData.setData(result);
                stageExecutionEventGetVO.setData(formData);
                stageExecutionEventGetVO.setExplainList(explainList);
                stageExecutionEventGetVO.setPaasFormEntity(paasFormEntity);
                break;
            case LINK_DATA_EDIT:
                ExecutionEventLinkDataPojo linkData = executionEventPojo.getLinkData();
                Long linkDataFormId = linkData.getFormId();
                String attr = linkData.getAttr();
                Integer linkDataSaasMark = linkData.getSaasMark();
                Integer linkDataBusinessType = linkData.getBusinessType();
                List<ExecutionEventFieldListPojo> linkDataFieldList = linkData.getFieldList();
                Long linkDataId = data.getLong(attr);
                explain = proFormHelp.getExplainEntity(corpid, linkDataFormId, linkDataBusinessType);
                explainMap = ExplainUtil.getExplainMap(explain.getExplains(), null);
                explainList = JSONArray.parseArray(explain.getExplains(), FieldAttrEntity.class);
                FieldAttrEntity linkDataFieldAttrEntity = ExplainUtil.getExplainMap(paasFormExplainModel.getByFormId(formId, corpid).getExplains(), null).get(attr);
                if (linkDataFieldAttrEntity == null) {
                    throw new XbbException(CustomerStageErrorCodeEnum.API_ERROR_238036);
                }
                // 关联数据
                PaasFormDataEntityExt linkDataPaasFormDataEntityExt = null;
                if (Objects.isNull(linkDataId) || linkDataId == 0) {
                    //找不到关联数据！
                    throw new XbbException(CustomerStageErrorCodeEnum.API_ERROR_238037);
                }
                if (Objects.equals(linkDataSaasMark, SaasMarkEnum.SAAS.getCode())) {
                    linkDataPaasFormDataEntityExt = proGetHelp.getByKey(linkDataId, linkDataBusinessType, linkDataSaasMark, corpid);
                } else {
                    linkDataPaasFormDataEntityExt = paasFormDataModel.getByKey(linkDataId, corpid);
                }
                paasFormEntity = paasFormModel.getByKey(linkDataFormId, corpid);
                fieldList = linkData.getFieldList();
                showExplainMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                for (ExecutionEventFieldListPojo temp : fieldList) {
                    FieldAttrEntity fieldAttrEntity = explainMap.get(temp.getAttr());
                    if(Objects.isNull(fieldAttrEntity)){
                        continue;
                    }
                    fieldAttrEntity.setRequired(temp.getRequired());
                    showExplainMap.put(temp.getAttr(), fieldAttrEntity);
                }
                userMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                userAndDepartmentGetDTO = new UserAndDepartmentGetDTO();
                BeanUtil.copyProperties(stageExecutionEventGetDTO, userAndDepartmentGetDTO);
                userAndDepartmentGetDTO.setExplainList(explainList);
                departmentMap = commonHelp.getDepMap(userAndDepartmentGetDTO);
                handlerExplainDTO = new HandlerExplainDTO();
                BeanUtil.copyProperties(stageExecutionEventGetDTO, handlerExplainDTO, false);
                creatorId = linkDataPaasFormDataEntityExt.getCreatorId();
                handlerExplainDTO.setCreatorId(creatorId);
                handlerExplainDTO.setExplainList(explainList);
                // 获取协同人和负责人
                ownerAndCoUser = paasFormHelp.getOwnerIdAndCoUserId(handlerExplainDTO, linkDataSaasMark, linkDataBusinessType, subBusinessType, linkDataPaasFormDataEntityExt, userMap);

                parseSingleRowDataDTO = new ParseSingleRowDataDTO();
                BeanUtil.copyProperties(stageExecutionEventGetDTO, parseSingleRowDataDTO);
                parseSingleRowDataDTO.setDataJsonObject(linkDataPaasFormDataEntityExt.getData());
                parseSingleRowDataDTO.setExplainMap(explainMap);
                parseSingleRowDataDTO.setUserMap(userMap);
                parseSingleRowDataDTO.setDepartmentMap(departmentMap);
                parseSingleRowDataVO = formDataExecutionEventUpdateGetAnalysisDataHelp.parseSingleRowData(parseSingleRowDataDTO);
                if (parseSingleRowDataVO == null) {
                    throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.PARSE_SINGLE_ROW_DATA_FAILED);
                }
                // 没设置的字段不可见
                for (FieldAttrEntity temp : explainList) {
                    attr = temp.getAttr();
                    FieldAttrEntity fieldAttrEntity = showExplainMap.get(attr);
                    if (Objects.isNull(fieldAttrEntity)) {
                        temp.setVisible(0);
                    } else {
                        temp.setSort(fieldAttrEntity.getSort());
                        temp.setRequired(fieldAttrEntity.getRequired());
                    }
                }
                //先获取data数据
                formData = new PaasFormDataEntityExt();
                if (Objects.equals(linkDataSaasMark, SaasMarkEnum.SAAS.getCode())) {
                    formData = saasUpdateHelp.getFormData(linkDataId, corpid, linkDataBusinessType);
                } else {
                    // 没有saasMark标志的，现在全按paas应用来处理
                    formData = paasFormDataModel.getByKey(linkDataId, corpid);
                }
                linkResultDataVO = parseSingleRowDataVO.getLinkResultDataVO();
                result = parseSingleRowDataVO.getResult();
                if (linkResultDataVO != null && !linkResultDataVO.getLinkData().isEmpty() && !linkResultDataVO.getLinkDataSet().isEmpty()) {
                    FormDataUpdateGetDTO formDataGetDTO = new FormDataUpdateGetDTO();
                    BeanUtil.copyProperties(stageExecutionEventGetDTO, formDataGetDTO);
                    linkResultDataVO.setData(result);
                    linkResultDataVO.setExplainMap(explainMap);
                    linkResultDataVO.setUserMap(userMap);
                    linkResultDataVO.setDepartmentMap(departmentMap);
                    linkResultDataVO.setExplainList(explainList);
                    result = formDataLinkDataUpdateGetService.formDataLinkDataGet(formDataGetDTO, linkResultDataVO);
                }
                formData.setData(result);
                stageExecutionEventGetVO.setData(formData);
                stageExecutionEventGetVO.setExplainList(explainList);
                stageExecutionEventGetVO.setPaasFormEntity(paasFormEntity);
                break;
            default:
                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001);
        }
        return stageExecutionEventGetVO;
    }

    /**
     * 进入详情页，获取触发的阶段版本
     * 会有几种情况：
     * 1 data中有阶段流程id，那就是新产生的数据可以直接用，
     * 2.没有阶段流程id，但又阶段code，此时是老数据的情况（未刷的数据），需要用默认的迁移的阶段流程oldTransferVersion为1
     * 3.用启用的版本流程
     * date: 2021/12/31 10:28 上午
     *
     * @Author yingjf
     */
    protected StageProcessEntity getStageProcessEntity(StageGetDTO stageGetDTO, Long stageProcessId, String stage, Map<String, FieldAttrEntity> explainMap, PaasFormDataEntityExt paasFormDataEntityExt) throws XbbException {
        String corpid = stageGetDTO.getCorpid();
        Long formId = stageGetDTO.getFormId();
        Integer businessType = stageGetDTO.getBusinessType();
        Integer saasMark = stageGetDTO.getSaasMark();
        Integer approveType = stageGetDTO.getApproveType();

        StageProcessEntity stageProcessEntity = null;
        if (Objects.nonNull(stageProcessId)) {
            stageProcessEntity = stageProcessModel.getByKey(stageProcessId, corpid);
        } else {
            // fix 少了一段，stageCode不为空，stageProcessId为空的逻辑
            // fix 这里会空指针，可能不存在启用的版本
            // stage 线索和客户需要传
            if (StringUtil.isNotEmpty(stage)) {
                stageProcessEntity = stageProcessModel.getOldTransferVersionProcess(stageProcessId, stage, corpid, formId);
            } else {
                //获取套餐类型
                Integer feeType = packageHelp.getFeeType(corpid);
                // 是否审批阶段详情
                boolean isApprove = Objects.equals(approveType, BasicConstant.ONE) || Objects.equals(approveType, BasicConstant.TWO);
                FieldAttrEntity fieldAttr = explainMap.values().stream().filter(field -> Objects.equals(field.getFieldType(), FieldTypeEnum.STAGE_THRUSTER.getType())).findFirst().orElse(null);
                if (!isApprove && Objects.nonNull(fieldAttr) && Objects.equals(fieldAttr.getStageMultiVersion(), 1) && Objects.equals(feeType, PackageTypeEnum.ULTIMATE.getType())) {
                    // 多版本启用，获取第一个满足触发条件的版本
                    List<StageProcessEntity> stageProcessList = stageProcessModel.getEnableStageProcessList(corpid, formId);
                    for (StageProcessEntity stageProcess : stageProcessList) {
                        JSONArray triggerCondition = stageProcess.getTriggerCondition();
                        boolean matchTriggerCondition;
                        if (CollectionsUtil.isEmpty(triggerCondition)) {
                            matchTriggerCondition = true;
                        } else {
                            List<FilterConditionsPoJo> triggerConditionPojos = triggerCondition.toJavaList(FilterConditionsPoJo.class);
                            BusinessRuleInfoPojo businessRuleInfoPojo = new BusinessRuleInfoPojo(corpid, formId, businessType, saasMark, explainMap);
                            matchTriggerCondition = businessRuleConditionHelp.validatePreCondition(triggerConditionPojos, paasFormDataEntityExt, businessRuleInfoPojo);
                        }
                        if (matchTriggerCondition) {
                            stageProcessEntity = stageProcess;
                            break;
                        }
                    }
                } else {
                    stageProcessEntity = stageProcessModel.getEnableStageProcess(corpid, formId);
                }
            }
        }
        return stageProcessEntity;
    }

    @Override
    public StartJumpVO startJump(StartJumpDTO startJumpDTO) throws XbbException {
        CommonJumpDTO commonJumpDTO = new CommonJumpDTO();
        BeanUtils.copyProperties(startJumpDTO, commonJumpDTO);
        CommonJumpVO commonJumpVO = commonJump(commonJumpDTO);
        StartJumpVO startJumpVO = new StartJumpVO();
        BeanUtils.copyProperties(commonJumpVO, startJumpVO);
        return startJumpVO;
    }

    /**
     * description: 通用跳转
     * date: 2021/12/9 11:12 上午
     *
     * @Author yingjf
     */
    private CommonJumpVO commonJump(CommonJumpDTO commonJumpDTO) throws XbbException {
        CommonJumpVO commonJumpVO = new CommonJumpVO();
        commonJumpVO.setMsg(I18nMessageUtil.getMessage(MessageConstant.FORM_ADD_RETURN_MSG));
        commonJumpVO.setCode(PaasConstant.FORM_ADD_RETURN_CODE);
        commonJumpVO.setFormDataId(commonJumpDTO.getDataId());
        String corpid = commonJumpDTO.getCorpid();
        Integer saasMark = commonJumpDTO.getSaasMark();
        Integer businessType = commonJumpDTO.getBusinessType();
        Long dataId = commonJumpDTO.getDataId();
        Long formId = commonJumpDTO.getFormId();
        Integer toStageType = commonJumpDTO.getToStageType();
        StageProcessEntity stageProcessEntity = commonJumpDTO.getStageProcessEntity();
        Long stageProcessId = stageProcessEntity.getId();
        PaasStageEntity toStageEntity = commonJumpDTO.getToStageEntity();
        PaasStageEntity fromStageEntity = commonJumpDTO.getFromStageEntity();
        Boolean isNew = commonJumpDTO.getIsNew();
        String userId = commonJumpDTO.getUserId();
        if (Objects.isNull(commonJumpDTO.getLoginUser()) && !Objects.isNull(userId)) {
            if (Objects.equals(commonJumpDTO.getFromOuterLink(), BasicConstant.ONE)) {
                commonJumpDTO.setLoginUser(null);
                commonJumpDTO.setLoginUserName("");
            }else {
                // 下面的日志需要loginuser    审批工作流那边需要传processCreatorId过来
                UserVO userVO = userService.getUserVO(corpid, userId, true, true);
                commonJumpDTO.setLoginUser(userVO);
                commonJumpDTO.setLoginUserName(userVO.getName());
            }
        }
        StageExecuteHandle stageExecute = stageExecuteFactory.getStageExecute(saasMark, businessType);
        StageJumpHandle stageJump = stageJumpFactory.getStageJump(toStageType);
        PaasFormDataEntityExt paasFormDataEntityExt = (PaasFormDataEntityExt) proModelService.getFormDataEntity(corpid, saasMark, businessType, dataId);
        PaasFormExplainEntity explain = paasFormExplainModel.getByFormId(formId, corpid);
        if (Objects.isNull(explain)) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.FORM_EXPLAIN_NOT_EXIST);
        }
        FieldAttrEntity passStageAttr = ExplainUtil.getPassStageAttr(explain);
        if (Objects.isNull(passStageAttr)) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.THIS_EXPLAIN_NOT_EXIST);
        }
        String stageAttr = passStageAttr.getAttr();
        String stageAttrName = passStageAttr.getAttrName();
        commonJumpDTO.setStageAttrName(stageAttrName);
        Integer noApprovalRequired = stageExecute.approvalRequired(commonJumpDTO, paasFormDataEntityExt, stageAttr);
        if (Objects.equals(noApprovalRequired, 1)) {
            StageJumpDealDTO stageJumpDealDTO = new StageJumpDealDTO();
            StageJumpSpecialDealDTO stageJumpSpecialDealDTO = new StageJumpSpecialDealDTO();
            BeanUtil.copyProperties(commonJumpDTO, stageJumpDealDTO);
            stageJumpDealDTO.setStageAttr(stageAttr);
            stageJumpDealDTO.setData(paasFormDataEntityExt.getData());
            // 阶段跳转处理
            stageJump.jumpDeal(stageJumpDealDTO);
            //开始处理特殊逻辑,新建的不走特殊逻辑处理
            if (Objects.isNull(isNew) || !isNew) {
                BeanUtils.copyProperties(stageJumpDealDTO, stageJumpSpecialDealDTO);
                stageExecute.jumpSpecialDeal(stageJumpSpecialDealDTO);
                // 跳转后的处理 日志 消息推送异步处理
                stageExecute.afterJumpDeal(stageJumpDealDTO, paasFormDataEntityExt);
                dynamicHelp.changeStage( corpid,  saasMark,  businessType,  dataId,
                        userId,formId, paasFormDataEntityExt,  commonJumpDTO);
            }
            if (Objects.nonNull(fromStageEntity)) {
                // 只支持旗舰版
                Integer feeType = commonHelp.getCurrentCompanyPackage(corpid);
                if (Objects.equals(feeType, PackageTypeEnum.ULTIMATE.getType())) {
                    // 处理没有表单触发工作流直接触发阶段的自定义工作流的情况
                    // 跳转到到阶段id
                    Long toStageId = toStageEntity.getId();
                    // 跳过来的阶段id
                    Long fromStageId = fromStageEntity.getId();

                    // 处理阶段推进后
                    Map<String, Object> param = new HashMap<>();
                    param.put(ParameterConstant.CORPID, corpid);
                    param.put(ParameterConstant.DEL, DelEnum.NORMAL.getDel());
                    param.put(ParameterConstant.FORMID, formId);
                    param.put("relatedStageId", toStageId);
                    param.put("stageId", fromStageId);
                    param.put("triggerType", StageWorkflowTriggerTypeEnum.AFTER_STAGE.getCode());
                    param.put("stageProcessId", stageProcessId);

                    List<StageWorkflowEntity> afterStageList = stageWorkflowModel.findEntitys(param);

                    if (CollectionsUtil.isNotEmpty(afterStageList)) {
                        // 发起工作流
                        startWorkflowFromStage(commonJumpDTO, corpid, stageAttr, stageExecute, paasFormDataEntityExt, afterStageList, WorkflowExecutionTypeEnum.AFTER_STAGE);
                    } else {
                        // 处理阶段到达前
                        param.put("relatedStageId", fromStageId);
                        param.put("stageId", toStageId);
                        param.put("triggerType", StageWorkflowTriggerTypeEnum.BEFORE_STAGE.getCode());
                        param.put("stageProcessId", stageProcessId);

                        List<StageWorkflowEntity> beforeStageList = stageWorkflowModel.findEntitys(param);
                        if (CollectionsUtil.isNotEmpty(beforeStageList)) {
                            // 发起工作流
                            startWorkflowFromStage(commonJumpDTO, corpid, stageAttr, stageExecute, paasFormDataEntityExt, beforeStageList, WorkflowExecutionTypeEnum.BEFORE_STAGE);
                        }
                    }
                }

            }

        } else if (Objects.equals(noApprovalRequired, 2)) {
            // 自动拒绝
            commonJumpVO.setMsg(I18nMessageUtil.getMessage(MessageConstant.FORM_ADD_PROCESS_RETURN_MSG));
            commonJumpVO.setCode(PaasConstant.FORM_ADD_PROCESS_RETURN_CODE);
        }else if (Objects.equals(noApprovalRequired, 4)) {
            // 无需进入审批节点，但是走了数据处理节点最终会在消费者完成剩余动作
            return commonJumpVO;
        } else {
            commonJumpVO.setMsg(I18nMessageUtil.getMessage(MessageConstant.FORM_ADD_PROCESS_RETURN_MSG));
            commonJumpVO.setCode(PaasConstant.FORM_ADD_PROCESS_RETURN_CODE);
            PaasProcessDataEntity paasProcessDataEntity = new PaasProcessDataEntity();
            paasProcessDataEntity.setCorpid(corpid);
            paasProcessDataEntity.setFormDataId(dataId);
            paasProcessDataEntity.setBusinessType(businessType);
            paasProcessDataEntity.setSaasMark(saasMark);
            proModelService.updateFormDataStatus(paasProcessDataEntity, FlowStatusEnum.APPROVAL_PENDING.getType());
        }

        return commonJumpVO;
    }

    /**
     * 新建、新建进审批（老审批/工作流审批），跳转进审批（老审批/工作流审批）这三个特殊场景的阶段跳转,和普通阶段跳转的逻辑处理分离开来
     *
     * @param commonJumpDTO
     * @return CommonJumpVO
     * @throws XbbException
     */
    @Override
    public CommonJumpVO stageCommonJump(CommonJumpDTO commonJumpDTO) throws XbbException {
        CommonJumpVO commonJumpVO = new CommonJumpVO();
        commonJumpVO.setMsg(I18nMessageUtil.getMessage(MessageConstant.FORM_ADD_RETURN_MSG));
        commonJumpVO.setCode(PaasConstant.FORM_ADD_RETURN_CODE);
        commonJumpVO.setFormDataId(commonJumpDTO.getDataId());
        String corpid = commonJumpDTO.getCorpid();
        Integer saasMark = commonJumpDTO.getSaasMark();
        Integer businessType = commonJumpDTO.getBusinessType();
        Long dataId = commonJumpDTO.getDataId();
        Long formId = commonJumpDTO.getFormId();
        Integer toStageType = commonJumpDTO.getToStageType();
        StageProcessEntity stageProcessEntity = commonJumpDTO.getStageProcessEntity();
        Long stageProcessId = stageProcessEntity.getId();
        PaasStageEntity toStageEntity = commonJumpDTO.getToStageEntity();
        PaasStageEntity fromStageEntity = commonJumpDTO.getFromStageEntity();
        Boolean isNew = commonJumpDTO.getIsNew();
        String userId = commonJumpDTO.getUserId();
        if (Objects.isNull(commonJumpDTO.getLoginUser()) && !Objects.isNull(userId)) {
            if (Objects.equals(commonJumpDTO.getFromOuterLink(), BasicConstant.ONE)) {
                commonJumpDTO.setLoginUser(null);
                commonJumpDTO.setLoginUserName("");
            }else {
                // 下面的日志需要loginuser    审批工作流那边需要传processCreatorId过来
                UserVO userVO = userService.getUserVO(corpid, userId, true, true);
                commonJumpDTO.setLoginUser(userVO);
                commonJumpDTO.setLoginUserName(userVO.getName());
            }
        }
        StageExecuteHandle stageExecute = stageExecuteFactory.getStageExecute(saasMark, businessType);
        StageJumpHandle stageJump = stageJumpFactory.getStageJump(toStageType);
        PaasFormDataEntityExt paasFormDataEntityExt = (PaasFormDataEntityExt) proModelService.getFormDataEntity(corpid, saasMark, businessType, dataId);
        PaasFormExplainEntity explain = paasFormExplainModel.getByFormId(formId, corpid);
        if (Objects.isNull(explain)) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.FORM_EXPLAIN_NOT_EXIST);
        }
        FieldAttrEntity passStageAttr = ExplainUtil.getPassStageAttr(explain);
        if (Objects.isNull(passStageAttr)) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.THIS_EXPLAIN_NOT_EXIST);
        }
        String stageAttr = passStageAttr.getAttr();
        String stageAttrName = passStageAttr.getAttrName();
        commonJumpDTO.setStageAttrName(stageAttrName);
        // 审批和工作流处理,入参封装  不用业务区分
        packageApprovalRequiredParams(corpid, saasMark, businessType, formId, stageProcessId, toStageEntity, fromStageEntity, commonJumpDTO);
        StageJumpDealDTO stageJumpDealDTO = new StageJumpDealDTO();
        StageJumpSpecialDealDTO stageJumpSpecialDealDTO = new StageJumpSpecialDealDTO();
        BeanUtil.copyProperties(commonJumpDTO, stageJumpDealDTO);
        if (Objects.nonNull(paasFormDataEntityExt)) {
            stageJumpDealDTO.setData(paasFormDataEntityExt.getData());
        }
        stageJumpDealDTO.setStageAttr(stageAttr);
        // 阶段跳转处理
        stageJump.jumpDeal(stageJumpDealDTO);
        //开始处理特殊逻辑,新建的不走特殊逻辑处理
        if (Objects.isNull(isNew) || !isNew) {
            BeanUtils.copyProperties(stageJumpDealDTO, stageJumpSpecialDealDTO);
            stageExecute.jumpSpecialDeal(stageJumpSpecialDealDTO);
            // 跳转后的处理 日志 消息推送异步处理
            stageExecute.afterJumpDeal(stageJumpDealDTO, paasFormDataEntityExt);
            dynamicHelp.changeStage( corpid,  saasMark,  businessType,  dataId,
                    userId,formId, paasFormDataEntityExt,  commonJumpDTO);
        }
        return commonJumpVO;
    }

    private void startWorkflowFromStage(CommonJumpDTO commonJumpDTO, String corpid, String stageAttr, StageExecuteHandle stageExecute, PaasFormDataEntityExt paasFormDataEntityExt, List<StageWorkflowEntity> afterStageList, WorkflowExecutionTypeEnum workflowExecutionTypeEnum) throws XbbException {
        List<Long> workflowIds = afterStageList.stream().map(StageWorkflowEntity::getWorkflowId).collect(Collectors.toList());
        Map<String, Object> params = new HashMap<>();
        params.put(ParameterConstant.CORPID, corpid);
        params.put(ParameterConstant.DEL, DelEnum.NORMAL.getDel());
        params.put(ParameterConstant.ID_IN, workflowIds);
        params.put(ParameterConstant.ENABLE, BasicConstant.ONE);
        List<WorkflowEntity> workflowModelEntitys = workflowModel.findEntitys(params);

        workflowIds = workflowModelEntitys.stream().map(WorkflowEntity::getId).collect(Collectors.toList());

        params.clear();
        params.put(ParameterConstant.CORPID, corpid);
        params.put(ParameterConstant.DEL, DelEnum.NORMAL.getDel());
        params.put("type", WorkflowNodeTypeEnum.START_NODE.getType());
        params.put("workflowIdIn", workflowIds);
        List<WorkflowNodeEntity> workflowNodeEntityList = workflowNodeModel.findEntitys(params);

        WorkflowExecutionEntity workflowExecutionEntity = new WorkflowExecutionEntity();
        workflowExecutionEntity.setCorpid(corpid);
        workflowExecutionEntity.setStatus(WorkflowExecutionStatusEnum.FINISH.getCode());
        workflowExecutionEntity.setType(WorkflowExecutionTypeEnum.DEFAULT_TYPE.getCode());
        workflowExecutionEntity.setTaskId(0L);
        String uuid = UUID.randomUUID().toString().replaceAll("-", "");
        workflowExecutionEntity.setLinkKey(uuid);

        long now = DateTimeUtil.getInt();
        workflowExecutionEntity.setAddTime(now);
        workflowExecutionEntity.setUpdateTime(now);
        workflowExecutionEntity.setDel(DelEnum.NORMAL.getDel());
        workflowExecutionEntity.setParentId(0L);

        workflowExecutionModel.insert(workflowExecutionEntity);

        // redis设置并行数量缓存值
        if (Objects.equals(workflowExecutionTypeEnum, WorkflowExecutionTypeEnum.AFTER_STAGE)) {
            // 阶段后
            String key = corpid + StringConstant.UNDER_LINE + uuid + StringConstant.UNDER_LINE + commonJumpDTO.getFromStageEntity().getId();
            paasRedisHelper.setValue(RedisPrefixConstant.WORKFLOW_CONCURRENT_NUM_LOCK, key, workflowNodeEntityList.size(), RedisTimeConstant.LONG_DURATION);
        }

        for (WorkflowNodeEntity workflowNodeEntity : workflowNodeEntityList) {
            stageExecute.workflowRequired(paasFormDataEntityExt, workflowNodeEntity, commonJumpDTO, workflowExecutionTypeEnum, stageAttr, workflowExecutionEntity);
        }
    }

    /*
     * 封装从审批和工作流过来的参数
     * date: 2022/1/19 4:16 下午
     * @Author yingjf
     */
    private void packageApprovalRequiredParams(String corpid, Integer saasMark, Integer businessType, Long formId, Long stageProcessId,
                                               PaasStageEntity toStageEntity, PaasStageEntity fromStageEntity, CommonJumpDTO commonJumpDTO) {
        Integer fromSort = 99;
        Integer toSort = toStageEntity.getSort();
        Map<String, Object> params = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        if (Objects.nonNull(fromStageEntity)) {
            fromSort = fromStageEntity.getSort();
        }
        params.put("corpid", corpid);
        params.put("formId", formId);
        params.put("stageProcessId", stageProcessId);
        params.put("enable", 1);
        params.put("del", 0);
        if (fromSort > toSort) {
            // 前进
            params.put("sortLess", fromSort);
            params.put("sortGreater", toSort);
        } else {
            // 后退
            params.put("sortLess", toSort);
            params.put("sortGreater", fromSort);
        }
        StageHandle stageHandle = stageHandleFactory.getStageHandle(saasMark, businessType);
        List<PaasStageEntity> paasStageEntityList = stageHandle.findEntitys(params);
        // 审批进来的需处理下入参
        commonJumpDTO.setRangeStageEntitys(CollectionsUtil.isEmpty(paasStageEntityList) ? new ArrayList<>() : paasStageEntityList);
    }

    /**
     * description:  计算公司平均停留时长，计算公式：平均时长计算：SUM（阶段停留时长）/ SUM（阶段数量），读取缓存，不支持实时计算
     * date: 2021/12/16 7:47 下午
     *
     * @Author yingjf
     */
    private Long calculateAvgStayTime(Integer saasMark, Integer businessType, String corpid, Long formId, Long stageProcessId, Long stageId) {
        Long stayTime;
        try {
            String redisKey = corpid + formId + stageProcessId + stageId;
            // 去redis获取用户信息
            String redisValue = paasRedisHelper.getValue(RedisConstant.STAGE_AVG_STAY_TIME, redisKey);
            if (StringUtil.isNotEmpty(redisValue) && !Objects.equals(redisValue, "null")) {
                stayTime = Long.parseLong(redisValue);
            } else {
                StageLogHandle stageLogHandle = stageLogHandleFactory.getStageLogHandle(saasMark, businessType);
                stayTime = stageLogHandle.getAvgStayTime(corpid, stageProcessId, stageId);
                if (Objects.nonNull(stayTime)) {
                    // 将查出来的用户信息塞入redis，时效为1小时
                    paasRedisHelper.setValue(RedisConstant.STAGE_AVG_STAY_TIME, redisKey, stayTime, RedisConstant.ONE_HOUR);
                }
            }
        } catch (Exception e) {
            log.error("计算公司平均停留时长stayTime出错", e);
            stayTime = 0L;
        }
        return Objects.isNull(stayTime) ? 0L : stayTime;
    }

    /**
     * description:
     * date: 2021/12/22 3:49 下午
     *
     * @Author yingjf
     */
    private void finishWorkLog(StageFinishWorkDTO stageFinishWorkDTO, StageWorkEntity stageWorkEntity) throws XbbException {
        Runnable runnable = () -> {
            try {
                List<DetailLogPojo> detailArr = new ArrayList();
                Integer isFinished = stageFinishWorkDTO.getIsFinished();
                Integer saasMark = stageFinishWorkDTO.getSaasMark();
                Integer businessType = stageFinishWorkDTO.getBusinessType();
                Long dataId = stageFinishWorkDTO.getDataId();
                String corpid = stageFinishWorkDTO.getCorpid();
                Long formId = stageFinishWorkDTO.getFormId();
                String oldTitle = "";
                String newTitle = "";
                PaasFormDataEntityExt formDataEntity = (PaasFormDataEntityExt) proModelService.getFormDataEntity(corpid, saasMark, businessType, dataId);
                if (Objects.nonNull(isFinished) && Objects.equals(isFinished, 1)) {
                    oldTitle = I18nStringConstant.STAGE_WORK_NOT_FINISHED;
                    newTitle = I18nStringConstant.STAGE_WORK_FINISHED;
                } else {
                    oldTitle = I18nStringConstant.STAGE_WORK_FINISHED;
                    newTitle = I18nStringConstant.STAGE_WORK_NOT_FINISHED;
                }
                LogInfoDealPojo logInfoDealPojo = stageDetailHelp.dealLogInfo(corpid, formId, saasMark, businessType, formDataEntity);
                String opObjectName = logInfoDealPojo.getOpObjectName();
                OperateModuleTypeEnum operateModuleTypeEnum = logInfoDealPojo.getOperateModuleTypeEnum();
                String newMsg = String.format(SymbolConstant.BRACKETS_SQUARE_SOLID, I18nMessageUtil.getMessage(newTitle));
                String oldMsg = String.format(SymbolConstant.BRACKETS_SQUARE_SOLID, I18nMessageUtil.getMessage(oldTitle));
                detailArr.add(new DetailLogPojo(stageWorkEntity.getName() + "-" + I18nMessageUtil.getMessage(I18nStringConstant.STAGE_LOG_SUFFIX_STATE)
                        , stageWorkEntity.getName() + "-" + I18nMessageUtil.getMessage(I18nStringConstant.STAGE_LOG_SUFFIX_STATE), oldMsg, newMsg));
                PaasFormEntityExt paasFormEntityExt = paasFormModel.getByKey(stageFinishWorkDTO.getFormId(), stageFinishWorkDTO.getCorpid());
                String memo = String.format(I18nMessageUtil.getMessage(I18nStringConstant.STAGE_OPERATION_LOG), stageFinishWorkDTO.getLoginUserName(),
                        paasFormEntityExt.getName(), opObjectName);
                mongoLogHelp.buildLog4DetailArr(detailArr, stageFinishWorkDTO.getCorpid(), stageFinishWorkDTO.getUserId(), stageFinishWorkDTO.getLoginUserName(),
                        operateModuleTypeEnum, OperateTypeEnum.EDIT, stageFinishWorkDTO.getFormId().toString(), opObjectName, memo, stageFinishWorkDTO.getHttpHeader());
            } catch (Exception e) {
                log.error("阶段任务日志", e);
            }
        };
        threadPoolBeanConfig.addUpdateDelThreadPool().execute(runnable);
    }

    /**
     * description:  更新客户隐藏字段合同的签订时间
     * date: 2022/07/12 3:49 下午
     *
     * @Author lijiangren
     */
    private void updateCustomerContractSignTime( Long contractDataId, String corpid, JSONObject oldData, JSONObject data) throws XbbException {
        Runnable runnable = () -> {
            try {
                Long customerId = FastJsonHelper.getLongFromFormData(oldData, ContractEnum.LINK_CUSTOMER.getAttr());
                Long signTime = data.getLong(ContractEnum.SIGN_TIME.getAttr());
                if (Objects.isNull(customerId) || Objects.isNull(signTime) || Objects.isNull(contractDataId)) {
                    return;
                }
                publicSeniorRuleHelp.updateCustomerContractSignTime(signTime,corpid,customerId,contractDataId);
            } catch (Exception e) {
                log.error("更新客户隐藏字段合同的签订时间异常", e);
            }
        };
        threadPoolBeanConfig.addUpdateDelThreadPool().execute(runnable);
    }

    /**
     * description:
     * date: 2021/12/22 3:49 下午
     *
     * @Author yingjf
     */
    private void updateImagesOrAttachmentsLog(StageExecutionEventSaveDTO stageExecutionEventSaveDTO, StageWorkLogEntity source, ExecutionEventPojo executionEventPojo) throws XbbException {
        Integer executionEventType = stageExecutionEventSaveDTO.getExecutionEventType();
        if (Objects.isNull(executionEventType) || (!Objects.equals(executionEventType, ExecutionEventEnum.UPLOAD_IMAGE.getType()) && !Objects.equals(executionEventType, ExecutionEventEnum.UPLOAD_ATTACHMENT.getType()))) {
            // 非上传图片和附件的跳过
            return;
        }
        Runnable runnable = () -> {
            try {
                List<DetailLogPojo> detailArr = new ArrayList();
                Integer saasMark = stageExecutionEventSaveDTO.getSaasMark();
                Integer businessType = stageExecutionEventSaveDTO.getBusinessType();
                Long dataId = stageExecutionEventSaveDTO.getDataId();
                String corpid = stageExecutionEventSaveDTO.getCorpid();
                Long formId = stageExecutionEventSaveDTO.getFormId();
                String oldTitle = "";
                String newTitle = "";
                if (Objects.equals(executionEventType, ExecutionEventEnum.UPLOAD_IMAGE.getType())) {
                    oldTitle = source.getImage();
                    newTitle = stageExecutionEventSaveDTO.getImages().toString();
                } else if (Objects.equals(executionEventType, ExecutionEventEnum.UPLOAD_ATTACHMENT.getType())) {
                    List<PaasAttachmentEntityExt> newAttachments = stageExecutionEventSaveDTO.getAttachments();
                    StringBuffer newStr = new StringBuffer();
                    StringBuffer oldStr = new StringBuffer();
                    for (PaasAttachmentEntityExt temp : newAttachments) {
                        if (newStr.length() > 0) {
                            newStr.append("、");
                        }
                        newStr.append(temp.getFilename());
                    }
                    List<PaasAttachmentEntityExt> oldAttachments = JsonHelperUtil.parseArray(source.getAttachment(), PaasAttachmentEntityExt.class);
                    for (PaasAttachmentEntityExt temp : oldAttachments) {
                        if (oldStr.length() > 0) {
                            oldStr.append("、");
                        }
                        oldStr.append(temp.getFilename());
                    }
                    oldTitle = oldStr.toString();
                    newTitle = newStr.toString();
                }
                PaasFormDataEntityExt formDataEntity = (PaasFormDataEntityExt) proModelService.getFormDataEntity(corpid, saasMark, businessType, dataId);
                LogInfoDealPojo logInfoDealPojo = stageDetailHelp.dealLogInfo(corpid, formId, saasMark, businessType, formDataEntity);
                String opObjectName = logInfoDealPojo.getOpObjectName();
                OperateModuleTypeEnum operateModuleTypeEnum = logInfoDealPojo.getOperateModuleTypeEnum();
                detailArr.add(new DetailLogPojo(executionEventPojo.getName() + "-" + I18nMessageUtil.getMessage(I18nStringConstant.STAGE_LOG_SUFFIX_EXECUTION_EVENT)
                        , executionEventPojo.getName() + "-" + I18nMessageUtil.getMessage(I18nStringConstant.STAGE_LOG_SUFFIX_EXECUTION_EVENT), oldTitle, newTitle));
                PaasFormEntityExt paasFormEntityExt = paasFormModel.getByKey(stageExecutionEventSaveDTO.getFormId(), stageExecutionEventSaveDTO.getCorpid());
                String memo = String.format(I18nMessageUtil.getMessage(I18nStringConstant.STAGE_OPERATION_LOG), stageExecutionEventSaveDTO.getLoginUserName(),
                        paasFormEntityExt.getName(), opObjectName);
                mongoLogHelp.buildLog4DetailArr(detailArr, stageExecutionEventSaveDTO.getCorpid(), stageExecutionEventSaveDTO.getUserId(), stageExecutionEventSaveDTO.getLoginUserName(),
                        operateModuleTypeEnum, OperateTypeEnum.EDIT, stageExecutionEventSaveDTO.getFormId().toString(), opObjectName, memo, stageExecutionEventSaveDTO.getHttpHeader());
            } catch (Exception e) {
                log.error("阶段任务图片/附件日志", e);
            }
        };
        threadPoolBeanConfig.addUpdateDelThreadPool().execute(runnable);
    }

    private void fieldAttrOrLinkDataEditLog(StageExecutionEventSaveDTO stageExecutionEventSaveDTO, List<DetailLogPojo> detailArr, PaasFormDataEntityExt formDataEntity) throws XbbException {
        Integer executionEventType = stageExecutionEventSaveDTO.getExecutionEventType();
        if (Objects.isNull(executionEventType) || (!Objects.equals(executionEventType, ExecutionEventEnum.FIELDATTR_EDIT.getType()) &&
                !Objects.equals(executionEventType, ExecutionEventEnum.LINK_DATA_EDIT.getType()))) {
            // 非上传图片和附件的跳过
            return;
        }

        Runnable runnable = () -> {
            try {
                Integer saasMark = formDataEntity.getSaasMark();
                Integer businessType = formDataEntity.getBusinessType();
                String corpid = formDataEntity.getCorpid();
                Long formId = formDataEntity.getFormId();
                LogInfoDealPojo logInfoDealPojo = stageDetailHelp.dealLogInfo(corpid, formId, saasMark, businessType, formDataEntity);
                String opObjectName = logInfoDealPojo.getOpObjectName();
                OperateModuleTypeEnum operateModuleTypeEnum = logInfoDealPojo.getOperateModuleTypeEnum(); // 执行事件日志 NullPointerException
                PaasFormEntityExt paasFormEntityExt = paasFormModel.getByKey(formId, corpid);
                if (Objects.nonNull(detailArr)) {
                    PaasFormExplainEntity paasFormExplainEntity = paasFormExplainModel.getByFormId(formId, corpid);
                    Map<String, FieldAttrEntity> explainMap = ExplainUtil.getExplainMap(paasFormExplainEntity.getExplains());
                    for (DetailLogPojo logPojo : detailArr) {
                        FieldAttrEntity fieldAttrEntity = explainMap.get(logPojo.getAttr());
                        if (Objects.nonNull(fieldAttrEntity) && (Objects.equals(fieldAttrEntity.getFieldType(), FieldTypeEnum.COMBO.getType()) || Objects.equals(fieldAttrEntity.getFieldType(), FieldTypeEnum.RADIO_GROUP.getType()))) {
                            Map<String, String> dictionaryMap = commonHelp.listToMap(fieldAttrEntity.getItems());
                            String beforeValue = dictionaryMap.get(logPojo.getUpdataBefore());
                            if (StringUtil.isNotEmpty(beforeValue)) {
                                logPojo.setUpdataBefore(beforeValue);
                            }
                            String afterValue = dictionaryMap.get(logPojo.getUpdataAfter());
                            if (StringUtil.isNotEmpty(afterValue)) {
                                logPojo.setUpdataAfter(afterValue);
                            }
                        }
                    }
                    String memo = String.format(I18nMessageUtil.getMessage(I18nStringConstant.STAGE_OPERATION_LOG), stageExecutionEventSaveDTO.getLoginUserName(),
                            paasFormEntityExt.getName(), opObjectName);
                    mongoLogHelp.buildLog4DetailArr(detailArr, corpid, stageExecutionEventSaveDTO.getUserId(), stageExecutionEventSaveDTO.getLoginUserName(),
                            operateModuleTypeEnum, OperateTypeEnum.EDIT, formId.toString(), opObjectName, memo, stageExecutionEventSaveDTO.getHttpHeader());
                }
            } catch (Exception e) {
                log.error("执行事件日志", e);
            }
        };
        threadPoolBeanConfig.addUpdateDelThreadPool().execute(runnable);
    }

    /**
     * 获取附件格式的Image，并且填参
     * @param images  格式：["","",{"attachIndex":"xxx.html",。。。}]
     * @param source
     * @return
     * @throws XbbException
     */
    private List<PaasAttachmentEntityExt> getAttachmentImages(JSONArray images, StageWorkLogEntity source) throws XbbException {
        List<PaasAttachmentEntityExt> res = new ArrayList<>(5);
        List<PaasAttachmentEntityExt> sourceImages = JsonHelperUtil.parseArray(source.getImage(), PaasAttachmentEntityExt.class);
        Map<String, PaasAttachmentEntityExt> imageMap = sourceImages.stream().collect(Collectors.toMap(PaasAttachmentEntityExt::getAttachIndex, sourceImage -> sourceImage, (a, b) -> b));

        if (Objects.isNull(images)) {
            return res;
        }
        for (Object image : images) {
            if (JSONObject.toJSON(image) instanceof PaasAttachmentEntityExt || image instanceof Map) {
                PaasAttachmentEntityExt addImage = JsonHelperUtil.parseObject(JSON.toJSONString(image), PaasAttachmentEntityExt.class);
                if (Objects.isNull(addImage)) {
                    throw new XbbException(ErrorCodeEnum.API_ERROR_238072);
                }
                res.add(addImage);
            } else if (image instanceof String) {
                PaasAttachmentEntityExt oldImage;
                if (imageMap.containsKey(image.toString())) {
                    PaasAttachmentEntityExt old = imageMap.get(image);
                    oldImage = new PaasAttachmentEntityExt(old.getFilename(), old.getAttachIndex(), old.getExt(), old.getSize());
                } else {
                    oldImage = new PaasAttachmentEntityExt("", image.toString(), "", 0);
                }
                res.add(oldImage);
            }
        }
        return res;
    }

    /**
     * 获取 image 回显格式
     * @param stageWorkLogEntity
     * @return
     */
    private List<String> getImagesVO(StageWorkLogEntity stageWorkLogEntity) {
        List<String> res = new ArrayList<>();
        if (Objects.isNull(stageWorkLogEntity)) {
            return res;
        }

        List<PaasAttachmentEntityExt> imageAttachmentList = JsonHelperUtil.parseArray(stageWorkLogEntity.getImage(), PaasAttachmentEntityExt.class);
        if (!imageAttachmentList.isEmpty()) {
            res = imageAttachmentList.stream().map(PaasAttachmentEntityExt::getAttachIndex).collect(Collectors.toList());
        } else {
            res = JsonHelperUtil.parseArray(stageWorkLogEntity.getImage(), String.class);
        }
        return res;
    }

    private List<ExecutionEventFieldListPojo> getFieldList(JSONObject executionEvent, ExecutionEventEnum executionEventEnum) {
        ExecutionEventPojo executionEventPojo = JSON.toJavaObject(executionEvent, ExecutionEventPojo.class);
        switch (executionEventEnum) {
            case FIELDATTR_EDIT:
                return executionEventPojo.getFieldList();
            case LINK_DATA_EDIT:
                return executionEventPojo.getLinkData().getFieldList();
            default:
                return null;
        }
    }
}